Gambar dengan semua warna


433

Mirip dengan gambar di allrgb.com , buat gambar di mana setiap piksel adalah warna unik (tidak ada warna yang digunakan dua kali dan tidak ada warna yang hilang).

Berikan program yang menghasilkan gambar seperti itu, bersama dengan tangkapan layar atau file output (unggah sebagai PNG).

  • Buat gambar secara algoritmik.
  • Gambar harus 256 × 128 (atau kisi yang dapat screenshot dan disimpan pada 256 × 128)
  • Gunakan semua warna 15-bit *
  • Tidak ada input eksternal yang diizinkan (juga tidak ada permintaan web, URL, atau database)
  • Tidak ada gambar tertanam yang diizinkan (kode sumber yang merupakan gambar baik-baik saja, mis. Piet )
  • Dithering diizinkan
  • Ini bukan kontes kode pendek, meskipun mungkin memenangkan suara Anda.
  • Jika Anda benar-benar siap menghadapi tantangan, lakukan 512 × 512, 2048 × 1024 atau 4096 × 4096 (dengan penambahan 3 bit).

Skor adalah dengan suara. Pilih gambar paling indah yang dibuat oleh kode paling elegan dan / atau algoritma yang menarik.

Algoritme dua langkah, tempat Anda pertama kali menghasilkan gambar yang bagus dan kemudian memasangkan semua piksel ke salah satu warna yang tersedia, tentu saja diperbolehkan, tetapi tidak akan memenangkan poin elegan.

* Warna 15-bit adalah 32768 warna yang dapat dibuat dengan mencampurkan 32 warna merah, 32 warna hijau, dan 32 warna biru, semuanya dalam langkah yang sama dan jarak yang sama. Contoh: dalam gambar 24 bit (8 bit per saluran), kisaran per saluran adalah 0..255 (atau 0..224), jadi bagi menjadi 32 warna dengan spasi yang sama.

Agar sangat jelas, array piksel gambar harus permutasi, karena semua gambar yang mungkin memiliki warna yang sama, hanya di lokasi piksel yang berbeda. Saya akan memberikan permutasi sepele di sini, yang sama sekali tidak indah:

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

masukkan deskripsi gambar di sini

Pemenang

Karena 7 hari telah berakhir, saya menyatakan pemenang

Namun, tidak berarti, pikir ini sudah berakhir. Saya, dan semua pembaca, selalu menerima desain yang lebih keren. Jangan berhenti membuat.

Pemenang: fejesjoco dengan 231 suara


8
Ketika Anda mengatakan "Dithering diizinkan", apa maksud Anda? Apakah ini pengecualian untuk aturan "setiap piksel adalah warna yang unik"? Jika tidak, apa yang Anda izinkan yang dilarang?
Peter Taylor

1
Ini berarti Anda dapat menempatkan warna dalam suatu pola, jadi ketika dilihat dengan mata, mereka berbaur menjadi warna yang berbeda. Misalnya, lihat gambar "dengan jelas semua RGB" pada halaman allRGB, dan banyak lainnya di sana.
Mark Jeronimus

8
Saya benar-benar menemukan contoh permutasi sepele Anda cukup menyenangkan bagi mata.
Jason C

2
@ Zom-B Man, aku sangat suka posting ini. Terima kasih!
Jason C

7
Hasil / jawaban yang indah!
EthanB

Jawaban:


534

C #

Saya menempatkan piksel acak di tengah, dan kemudian mulai menempatkan piksel acak di lingkungan yang paling mirip dengan mereka. Dua mode didukung: dengan pemilihan minimum, hanya satu piksel tetangga dipertimbangkan pada satu waktu; dengan pilihan rata-rata, semua (1..8) rata-rata. Pilihan minimum agak bising, pemilihan rata-rata tentu saja lebih kabur, tetapi keduanya terlihat seperti lukisan sebenarnya. Setelah diedit, inilah versi terbaru yang agak dioptimalkan (bahkan menggunakan pemrosesan paralel!):

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 piksel, mulai dari tengah, pilihan minimum:

256x128 piksel, mulai dari sudut kiri atas, pilihan minimum:

256x128 piksel, mulai dari tengah, pilihan rata-rata:

Berikut adalah dua animgif 10-bingkai yang menunjukkan cara kerja pemilihan minimum dan rata-rata (pujian untuk format gif karena dapat menampilkannya dengan 256 warna saja):

Mode pilihan mimimum tumbuh dengan muka gelombang kecil, seperti gumpalan, mengisi semua piksel saat berjalan. Namun, dalam mode rata-rata, ketika dua cabang berwarna yang berbeda mulai tumbuh berdampingan, akan ada celah hitam kecil karena tidak ada yang cukup dekat dengan dua warna berbeda. Karena celah itu, muka gelombang akan menjadi urutan besarnya lebih besar, oleh karena itu algoritme akan jauh lebih lambat. Tapi itu bagus karena terlihat seperti karang yang tumbuh. Jika saya akan menjatuhkan mode rata-rata, itu bisa dibuat sedikit lebih cepat karena setiap warna baru dibandingkan dengan setiap piksel yang ada sekitar 2-3 kali. Saya tidak melihat cara lain untuk mengoptimalkannya, saya pikir itu sudah cukup bagus.

Dan daya tarik besar, inilah rendering 512x512 piksel, awal, pemilihan minimum:

Saya tidak bisa berhenti bermain dengan ini! Dalam kode di atas, warnanya diurutkan secara acak. Jika kami tidak mengurutkan sama sekali, atau mengurutkan berdasarkan rona ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue())), kami mendapatkan ini masing-masing (mulai dari awal dan minimum):

Kombinasi lain, di mana bentuk karang disimpan sampai akhir: rona dipesan dengan seleksi rata-rata, dengan animgif 30-bingkai:

UPDATE: ITU SIAP !!!

Anda ingin hi-res, saya ingin hi-res, Anda tidak sabar, saya hampir tidak tidur. Sekarang saya senang mengumumkan bahwa akhirnya sudah siap, kualitas produksi. Dan saya merilisnya dengan big bang, video YouTube 1080p yang luar biasa! Klik di sini untuk videonya , mari buat viral untuk mempromosikan gaya geek. Saya juga memposting hal-hal di blog saya di http://joco.name/ , akan ada posting teknis tentang semua detail menarik, optimasi, bagaimana saya membuat video, dll. Dan akhirnya, saya membagikan sumbernya kode di bawah GPL. Ini menjadi besar sehingga hosting yang tepat adalah tempat terbaik untuk ini, saya tidak akan mengedit bagian atas jawaban saya lagi. Pastikan untuk mengkompilasi dalam mode rilis! Program ini menskala dengan baik ke banyak core CPU. Render 4Kx4K membutuhkan sekitar 2-3 GB RAM.

Sekarang saya dapat membuat gambar besar dalam 5-10 jam. Saya sudah memiliki beberapa render 4Kx4K, saya akan mempostingnya nanti. Program ini telah banyak maju, ada optimasi yang tak terhitung jumlahnya. Saya juga membuatnya ramah pengguna sehingga siapa pun dapat dengan mudah menggunakannya, ia memiliki baris perintah yang bagus. Program ini juga bersifat acak deterministik, yang berarti, Anda dapat menggunakan seed acak dan itu akan menghasilkan gambar yang sama setiap waktu.

Berikut adalah beberapa render besar.

512 favorit saya:


(sumber: joco.name )

2048-an yang muncul di video saya :


(sumber: joco.name )


(sumber: joco.name )


(sumber: joco.name )


(sumber: joco.name )

Yang pertama dibuat 4096 (TODO: mereka sedang diunggah, dan situs web saya tidak dapat menangani lalu lintas besar, sehingga mereka dipindahkan sementara):


(sumber: joco.name )


(sumber: joco.name )


(sumber: joco.name )


(sumber: joco.name )


25
Sekarang ini keren!
Jaa-c

5
Sangat bagus :-D Sekarang buat yang lebih besar!
Lubang keras melengking

20
Anda seorang seniman sejati! :)
AL

10
Berapa harga untuk cetak?
primo

16
Saya sedang mengerjakan render besar dan video 1080p. Akan butuh berjam-jam atau berhari-hari. Saya berharap seseorang akan dapat membuat cetakan dari render besar. Atau bahkan kaos: kode di satu sisi, gambar di sisi lain. Adakah yang bisa mengaturnya?
fejesjoco

248

Pengolahan

Memperbarui! Gambar 4096x4096!

Saya telah menggabungkan posting kedua saya menjadi yang satu ini dengan menggabungkan dua program bersama.

Koleksi lengkap gambar yang dipilih dapat ditemukan di sini, di Dropbox . (Catatan: DropBox tidak dapat membuat pratinjau untuk gambar 4096x4096; klik saja lalu klik "Unduh").

Jika Anda hanya melihat sekali lihat yang satu ini (tileable)! Ini diperkecil (dan banyak lagi di bawah), asli 2048x1024:

masukkan deskripsi gambar di sini

Program ini bekerja dengan jalan setapak dari titik yang dipilih secara acak dalam kubus warna, kemudian menggambar mereka ke jalur yang dipilih secara acak dalam gambar. Ada banyak kemungkinan. Opsi yang dapat dikonfigurasi adalah:

  • Panjang maksimum jalur kubus warna.
  • Langkah maksimum untuk mengambil kubus warna (nilai yang lebih besar menyebabkan varians yang lebih besar tetapi meminimalkan jumlah jalur kecil menuju akhir ketika semuanya menjadi ketat).
  • Ubin gambar.
  • Saat ini ada dua mode jalur gambar:
    • Mode 1 (mode tulisan asli ini): Menemukan blok piksel yang tidak digunakan dalam gambar dan menjadikannya blok. Blok dapat ditempatkan secara acak, atau dipesan dari kiri ke kanan.
    • Mode 2 (mode posting kedua yang saya gabungkan menjadi yang ini): Memilih titik awal acak pada gambar dan berjalan di sepanjang jalur melalui piksel yang tidak digunakan; dapat berjalan di sekitar piksel yang digunakan. Opsi untuk mode ini:
      • Kumpulan arahan untuk berjalan (ortogonal, diagonal, atau keduanya).
      • Apakah atau tidak mengubah arah (saat ini searah jarum jam tetapi kode fleksibel) setelah setiap langkah, atau hanya mengubah arah setelah menemukan piksel yang diduduki ..
      • Opsi untuk mengacak urutan perubahan arah (bukan searah jarum jam).

Ia bekerja untuk semua ukuran hingga 4096x4096.

Sketsa Pemrosesan lengkap dapat ditemukan di sini: Tracer.zip

Saya telah menempelkan semua file dalam blok kode yang sama di bawah ini hanya untuk menghemat ruang (bahkan semua dalam satu file, ini masih berupa sketsa yang valid). Jika Anda ingin menggunakan salah satu preset, ubah indeks dalam gPresettugas. Jika Anda menjalankan ini dalam Memproses Anda dapat menekan rsaat sedang berjalan untuk menghasilkan gambar baru.

  • Pembaruan 1: Kode yang dioptimalkan untuk melacak warna / piksel yang tidak digunakan pertama dan tidak mencari lebih dari piksel yang diketahui digunakan; mengurangi waktu pembuatan 2048x1024 dari 10-30 menit menjadi sekitar 15 detik, dan 4096x4096 dari 1-3 jam menjadi sekitar 1 menit. Sumber dan sumber drop box di bawah diperbarui.
  • Pembaruan 2: Memperbaiki bug yang mencegah 4096x4096 gambar dihasilkan.
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;
  }

}

Berikut ini adalah set lengkap 256x128 gambar yang saya sukai:

Mode 1:

Favorit saya dari set asli (max_path_length = 512, path_step = 2, acak, ditampilkan 2x, tautan 256x128 ):

masukkan deskripsi gambar di sini

Lainnya (kiri dua dipesan, kanan dua acak, dua panjang lintasan terbatas, dua terbawah tidak dibatasi):

ordlimit Randand ordnolimit Randandolimit

Yang ini bisa ubin:

Randand

Mode 2:

berlian bunga-bunga boxfade diagover bigdiamonds kotak2 pecahan

Yang ini bisa ubin:

bigtile berliantile pembungkus kado

512x512 pilihan:

Berlian Tileable, favorit saya dari mode 2; Anda dapat melihat di sini bagaimana jalur berjalan di sekitar objek yang ada:

masukkan deskripsi gambar di sini

Langkah jalur lebih besar dan panjang jalur maks, ubin:

masukkan deskripsi gambar di sini

Mode acak 1, ubin:

masukkan deskripsi gambar di sini

Lebih banyak pilihan:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Semua rendering 512x512 dapat ditemukan di folder dropbox (* _64.png).

2048x1024 dan 4096x4096:

Ini terlalu besar untuk disematkan dan semua host gambar yang saya temukan turunkan ke 1600x1200. Saat ini saya sedang merender set gambar 4096x4096 sehingga lebih banyak akan segera tersedia. Alih-alih memasukkan semua tautan di sini, cukup periksa di folder dropbox (* _128.png dan * _256.png, perhatikan: 4096x4096 terlalu besar untuk pratinjau dropbox, cukup klik "unduh"). Berikut adalah beberapa favorit saya:

2048x1024 berlian ubin besar (sama dengan yang saya tautkan pada awal posting ini)

2048x1024 berlian (Saya suka yang ini!), Diperkecil:

masukkan deskripsi gambar di sini

4096x4096 berlian ubin besar (Akhirnya! Klik 'unduh' di tautan Dropbox; terlalu besar untuk pratinjau mereka), diperkecil dengan cara menurun:

4096x4096 berlian ubin besar

Mode acak 4096x4096 1 : masukkan deskripsi gambar di sini

4096x4096 satu lagi yang keren

Pembaruan: Kumpulan gambar prasetel 2048x1024 selesai dan di dropbox. Set 4096x4096 harus dilakukan dalam waktu satu jam.

Ada banyak sekali yang bagus, saya kesulitan memilih yang mana untuk dikirim, jadi silakan periksa tautan folder!


6
Itu mengingatkan saya pada pandangan close-up dari beberapa mineral.
Morwenn

3
Bukan bagian dari kontes, tetapi saya pikir ini agak keren ; Saya menerapkan gaussian blur besar dan peningkatan kontras otomatis ke salah satu mode 1 foto acak di photoshop dan itu membuat semacam latar belakang desktop yang bagus.
Jason C

2
whoa, ini adalah gambar keren!
sevenseacat

2
Mengingatkan saya pada tekstur Gustav Klimt.
Kim

2
Tahukah Anda bahwa Anda dapat men-hotlink gambar di Dropbox? Hanya menyalin URL download, menghapus dl=1dan token_hash=<something>bagian dan membuat link ke gambar Anda seperti ini: [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). Kiat lain: Anda dapat memampatkan gambar Anda (saya mendapatkan hasil yang baik dengan TruePNG ( Unduh )). Saya dapat menghemat 28,1% dari ukuran file pada gambar ini .
user2428118

219

Python w / PIL

Ini didasarkan pada Fraktal Newton , khusus untuk z → z 5 - 1 . Karena ada lima akar, dan dengan demikian lima titik konvergensi, ruang warna yang tersedia dibagi menjadi lima wilayah, berdasarkan Hue. Poin individu diurutkan pertama dengan jumlah iterasi yang diperlukan untuk mencapai titik konvergensi mereka, dan kemudian dengan jarak ke titik itu, dengan nilai sebelumnya diberi warna yang lebih terang.

Pembaruan: 4096x4096 render besar, di-host di allrgb.com .

Asli (33,7 MB)

Tampilan dekat dari pusat (ukuran sebenarnya):

Titik pandang yang berbeda menggunakan nilai-nilai ini:

xstart = 0
ystart = 0

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

Asli (32,2 MB)

Dan yang lain menggunakan ini:

xstart = 0.5
ystart = 0.5

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

Asli (27,2 MB)


Animasi

Berdasarkan permintaan, saya telah menyusun animasi zoom.

Titik Fokus: ( 0,50051 , -0,50051 )
Faktor zoom: 2 1/5

Titik fokus adalah nilai yang sedikit aneh, karena saya tidak ingin memperbesar titik hitam. Faktor zoom dipilih sedemikian rupa sehingga dua kali lipat setiap 5 frame.

Penggoda 32x32:

Versi 256x256 dapat dilihat di sini:
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

Mungkin ada titik-titik yang secara matematis memperbesar "ke diri mereka sendiri," yang akan memungkinkan untuk animasi tanpa batas. Jika saya dapat mengidentifikasi, saya akan menambahkannya di sini.


Sumber

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
Akhirnya fraktal :) Suka itu. Juga, warna hijau di 144 derajat adalah warna favorit saya (bukan hijau murni pada 120 derajat yang hanya membosankan).
Mark Jeronimus

2
Saya tidak tahu, sebenarnya saya lebih suka versi AllRGB; kebutuhan untuk menggunakan ruang pencahayaan penuh dengan baik menekankan gradien.
Ilmari Karonen

2
+1 Akhirnya beberapa fraktal yang bagus! Yang terakhir adalah favorit pribadi saya. Anda harus memperbesar video! (@Quincunx: Lihat milikmu juga; suaranya sudah mulai sejak hari pertama!)
Jason C

1
@JasonC Saya telah menambahkan animasi;)
primo

2
@primo Saya tahu saya terlambat, tetapi saya hanya ingin mengatakan gambar-gambar ini spektakuler.
Ashwin Gupta

130

Saya mendapat ide ini dari algoritma fejesjoco pengguna dan ingin bermain sedikit, jadi saya mulai menulis algoritma saya sendiri dari awal.

Saya memposting ini karena saya merasa bahwa jika saya dapat membuat sesuatu yang lebih baik * daripada yang terbaik dari kalian, saya pikir tantangan ini belum selesai. Sebagai perbandingan, ada beberapa desain yang menakjubkan pada allRGB yang saya pertimbangkan jauh melampaui level yang dicapai di sini dan saya tidak tahu bagaimana mereka melakukannya.

*) masih akan diputuskan oleh suara

Algoritma ini:

  1. Mulailah dengan (sedikit) biji, dengan warna sedekat mungkin ke hitam.
  2. Simpan daftar semua piksel yang belum dikunjungi dan terhubung ke titik yang dikunjungi.
  3. Pilih titik ** acak dari daftar itu
  4. Hitung warna rata-rata dari semua piksel yang dihitung [Sunting ... dalam kotak 9x9 menggunakan kernel Gaussian] yang terhubung 8 (ini adalah alasan mengapa terlihat sangat halus) Jika tidak ada yang ditemukan, ambil hitam.
  5. dalam kubus 3x3x3 di sekitar warna ini, cari warna yang tidak digunakan.
    • Ketika warna multple ditemukan, ambil yang paling gelap.
    • Ketika multple warna gelap sama-sama ditemukan, ambil satu acak dari mereka.
    • Ketika tidak ada yang ditemukan, perbarui rentang pencarian ke 5x5x5, 7x7x7, dll. Ulangi dari 5.
  6. Plot piksel, perbarui daftar, dan ulangi dari 3

Saya juga bereksperimen dengan probabilitas yang berbeda untuk memilih poin kandidat berdasarkan pada penghitungan berapa banyak tetangga yang dikunjungi piksel yang dipilih, tetapi hanya memperlambat algoritma tanpa membuatnya lebih cantik. Algoritma saat ini tidak menggunakan probabilitas dan memilih titik acak dari daftar. Hal ini menyebabkan poin dengan banyak tetangga cepat terisi, menjadikannya hanya bola solid yang tumbuh dengan tepi kabur. Ini juga mencegah tidak tersedianya warna tetangga jika celah-celah itu harus diisi kemudian dalam proses.

Gambar tersebut toroidal.

Jawa

Unduh: com.digitalmodularperpustakaan

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
  • 1 biji asli
  • 1 detik

masukkan deskripsi gambar di sini

  • 2048 × 1024
  • sedikit ubin untuk desktop 1920 × 1080
  • 30 detik
  • negatif di photoshop

masukkan deskripsi gambar di sini

  • 2048 × 1024
  • 8 biji
  • 27 detik

masukkan deskripsi gambar di sini

  • 512 × 512
  • 40 biji acak
  • 6 detik

masukkan deskripsi gambar di sini

  • 4096 × 4096
  • 1 biji
  • Coretan menjadi lebih tajam secara signifikan (karena kelihatannya mereka dapat memotong ikan menjadi sashimi)
  • Sepertinya selesai dalam 20 menit, tapi ... gagal menyelesaikannya karena suatu alasan, jadi sekarang aku menjalankan 7 instance secara paralel dalam semalam.

[Lihat di bawah]

[Sunting]
** Saya menemukan bahwa metode saya memilih piksel sama sekali tidak acak. Saya pikir memiliki permutasi acak dari ruang pencarian akan menjadi acak dan lebih cepat daripada acak nyata (karena suatu titik tidak akan dipilih dua kali secara kebetulan. Namun, entah bagaimana, menggantinya dengan acak nyata, saya secara konsisten mendapatkan lebih banyak bintik noise pada gambar saya.

[kode versi 2 dihapus karena saya melebihi batas 30.000 karakter]

masukkan deskripsi gambar di sini

  • Meningkatkan kubus pencarian awal menjadi 5x5x5

masukkan deskripsi gambar di sini

  • Bahkan lebih besar, 9x9x9

masukkan deskripsi gambar di sini

  • Kecelakaan 1. Menonaktifkan permutasi sehingga ruang pencarian selalu linier.

masukkan deskripsi gambar di sini

  • Kecelakaan 2. Mencoba teknik pencarian baru menggunakan antrian fifo. Masih harus menganalisis ini, tetapi saya pikir itu layak untuk dibagikan.

masukkan deskripsi gambar di sini

  • Selalu memilih dalam X piksel yang tidak digunakan dari pusat
  • X berkisar dari 0 hingga 8192 dalam langkah 256

Gambar tidak dapat diunggah: "Ups! Sesuatu yang Buruk Terjadi! Bukan Anda, ini kami. Ini salah kami." Gambar terlalu besar untuk imgur. Mencoba di tempat lain ...

masukkan deskripsi gambar di sini

Bereksperimen dengan paket penjadwal saya temukan di digitalmodularperpustakaan untuk menentukan urutan penanganan piksel (bukan difusi).

package demos;

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

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

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

    public static void main(String[] args) {

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

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

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

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

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

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

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

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

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

        new Thread(this).start();
    }

    @Override
    public void resized() {}

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

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

        // System.exit(0);
    }

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

        Arrays.fill(colorCube, false);

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

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

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

        timer.stop();
        draw();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return -1;
    }

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

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

masukkan deskripsi gambar di sini

  • Angular (64)

masukkan deskripsi gambar di sini

  • CRT

masukkan deskripsi gambar di sini

  • Gentar

masukkan deskripsi gambar di sini

  • Bunga (5, X), di mana X berkisar 0,5 hingga 20 dalam langkah X = X × 1.2

masukkan deskripsi gambar di sini

  • Mod

masukkan deskripsi gambar di sini

  • Pythagoras

masukkan deskripsi gambar di sini

  • Radial

masukkan deskripsi gambar di sini

  • Acak

masukkan deskripsi gambar di sini

  • Scanline

masukkan deskripsi gambar di sini

  • Spiral (X), di mana X berkisar dari 0,1 hingga 200 dalam langkah X = X × 1.2
  • Anda dapat melihatnya berkisar di antara Radial ke Angular (5)

masukkan deskripsi gambar di sini

  • Membagi

masukkan deskripsi gambar di sini

  • SquareSpiral

masukkan deskripsi gambar di sini

  • XOR

masukkan deskripsi gambar di sini

Makanan mata baru

  • Pengaruh pemilihan warna oleh max(r, g, b)

masukkan deskripsi gambar di sini

  • Pengaruh pemilihan warna oleh min(r, g, b)
  • Perhatikan bahwa yang ini memiliki fitur / detail yang persis sama dengan yang di atas, hanya dengan warna yang berbeda! (benih acak yang sama)

masukkan deskripsi gambar di sini

  • Pengaruh pemilihan warna oleh max(r, min(g, b))

masukkan deskripsi gambar di sini

  • Pengaruh pemilihan warna berdasarkan nilai abu-abu 299*r + 436*g + 114*b

masukkan deskripsi gambar di sini

  • Pengaruh pemilihan warna oleh 1*r + 10*g + 100*b

masukkan deskripsi gambar di sini

  • Pengaruh pemilihan warna oleh 100*r + 10*g + 1*b

masukkan deskripsi gambar di sini

  • Selamat kecelakaan saat 299*r + 436*g + 114*bmeluap dalam bilangan bulat 32-bit

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

  • Varian 3, dengan nilai abu-abu dan Penjadwal radial

masukkan deskripsi gambar di sini

  • Saya lupa bagaimana saya membuat ini

masukkan deskripsi gambar di sini

  • Penjadwal CRT juga memiliki bug integer overflow senang (memperbarui ZIP), ini menyebabkannya mulai setengah jalan, dengan 512 × 512 gambar, bukan di tengah. Seharusnya terlihat seperti ini:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

  • InverseSpiralScheduler(64) (baru)

masukkan deskripsi gambar di sini

  • XOR lain

masukkan deskripsi gambar di sini

  • 4096 pertama yang berhasil ditampilkan setelah perbaikan bug. Saya pikir ini versi 3 SpiralScheduler(1)atau sesuatu

masukkan deskripsi gambar di sini (50MB !!)

  • Versi 1 4096, tetapi saya tidak sengaja meninggalkan kriteria warna max()

masukkan deskripsi gambar di sini (50MB !!)

  • 4096, sekarang dengan min()
  • Perhatikan bahwa yang ini memiliki fitur / detail yang persis sama dengan yang di atas, hanya dengan warna yang berbeda! (benih acak yang sama)
  • Waktu: lupa merekamnya tetapi stempel waktu file adalah 3 menit setelah gambar sebelumnya

masukkan deskripsi gambar di sini (50MB !!)


Keren. Gambar terakhir Anda mirip dengan ide kedua yang telah saya bolak-balik, meskipun saya merasa milik saya tidak akan sebagus itu. BTW, ada yang keren serupa di allrgb.com/diffusive .
Jason C

Itu dimaksudkan hanya sebagai penggoda, tetapi saya mengeditnya karena takut ditandai, yang tampaknya terjadi :)
Mark Jeronimus

2
Bahkan kecelakaan terlihat bagus :). Kubus warna sepertinya ide yang sangat bagus, dan kecepatan render Anda luar biasa, dibandingkan dengan saya. Beberapa desain di allrgb memang memiliki deskripsi yang baik, misalnya allrgb.com/dla. Saya berharap saya memiliki lebih banyak waktu untuk melakukan lebih banyak eksperimen, ada begitu banyak kemungkinan ...
fejesjoco

Saya hampir lupa, saya baru saja mengunggah beberapa gambar besar saya. Saya pikir salah satu dari mereka, pelangi asap / tumpah tinta thingy, lebih baik daripada apa pun di allrgb :). Saya setuju, yang lain tidak begitu menakjubkan, itu sebabnya saya membuat video untuk membuat sesuatu yang lebih dari mereka :).
fejesjoco

Menambahkan kode sumber dan tautan ke perpustakaan Digisoft, sehingga Anda benar-benar dapat mengkompilasi kode saya
Mark Jeronimus

72

C ++ w / Qt

Saya melihat versi Anda:

masukkan deskripsi gambar di sini

menggunakan distribusi normal untuk warna:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

atau pertama kali diurutkan berdasarkan warna merah / rona (dengan deviasi yang lebih kecil):

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

atau distribusi lain:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Distribusi cauchy (hsl / red):

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

diurutkan cols berdasarkan lightness (hsl):

masukkan deskripsi gambar di sini

kode sumber yang diperbarui - menghasilkan gambar ke-6:

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

Bagus sekali. Namun, mungkin tidak image.pixel(x, y) == 0gagal dan menimpa piksel yang ditempatkan pertama?
Mark Jeronimus

@ Zom-B: bisa, tapi yang terakhir akan menjadi hitam, jadi sudah sesuai aturan ..
Jaa-c

Tidak ada masalah aturan sekalipun. Saya hanya berpikir Anda mungkin telah melewatkannya. Mungkin juga terhitung mulai 1 lalu. Saya suka yang lain!
Mark Jeronimus

@ Zom-B: terima kasih, saya dapat menambahkan beberapa lagi, saya agak menyukainya: P
Jaa-c

Yang satu dengan dua lingkaran dan yang di bawahnya bersama-sama terlihat seperti wajah monyet.
Jason C

64

Di Jawa:

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

}

dan gambar input:

pukang

Saya menghasilkan sesuatu seperti ini:

acidLemur

versi tidak terkompresi di sini: https://www.mediafire.com/?7g3fetvaqhoqgh8

Butuh komputer saya kira-kira 30 menit untuk melakukan gambar 4096 ^ 2, yang merupakan peningkatan besar selama 32 hari implementasi pertama saya akan diambil.


1
Aduh; 32 hari tidak terdengar lucu ..... algoritma rata-rata dalam jawaban fejesjocos pada 4k sebelum mengoptimalkan mungkin akan memakan waktu beberapa bulan
masterX244

5
Saya suka alis punknya!
Level River St

45

Java dengan BubbleSort

(biasanya Bubblesort tidak terlalu disukai tetapi untuk tantangan ini akhirnya digunakan :) menghasilkan garis dengan semua elemen dalam 4096 langkah terpisah kemudian mengocoknya; penyortiran pergi melalui dan masing-masing suka mendapat 1 ditambahkan ke nilai mereka saat sedang diurutkan sehingga Anda mendapat nilai diurutkan dan semua warna

Memperbarui Sourcecode untuk menghilangkan garis-garis besar itu
(perlu beberapa bitwise magic: P)

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

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

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

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

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

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

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

Hasil:

Versi lama

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

pratinjau keluaran


Sudah ada versi QuickSort di halaman allRGB.
Mark Jeronimus

1
@ Zom-B Quicksort adalah algoritma yang berbeda dari Bubblesort
masterX244

43

C

Membuat pusaran, untuk alasan yang saya tidak mengerti, dengan bingkai genap dan aneh berisi pusaran yang sama sekali berbeda.

Ini adalah pratinjau dari 50 frame aneh pertama:

pratinjau vortex

Contoh gambar yang dikonversi dari PPM untuk menunjukkan cakupan warna lengkap:

contoh gambar

Kemudian, ketika semuanya dicampur menjadi abu-abu, Anda masih bisa melihatnya berputar: urutan lebih lama .

Kode sebagai berikut. Untuk menjalankan, masukkan nomor bingkai, misalnya:

./vortex 35 > 35.ppm

Saya menggunakan ini untuk mendapatkan GIF animasi:

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
Anda tahu itu C ketika sesuatu terjadi karena "alasan saya tidak mengerti".
Nit

2
Ya, biasanya saya tahu apa yang diharapkan, tetapi di sini saya hanya bermain-main untuk melihat pola apa yang bisa saya dapatkan, dan urutan tanpa-penghentian kekacauan dalam urutan ini muncul.

8
Ini pusaran karena fungsi perbandingan Anda tidak mengikuti ketimpangan segitiga. Misalnya, r> b, b> g, g> r. Saya bahkan tidak bisa port ke Jawa karena itu mergesort bergantung pada properti ini, jadi saya mendapatkan pengecualian "Metode perbandingan melanggar kontrak umum!"
Mark Jeronimus

2
Saya akan mencoba p->b * 6 - q->g;tetapi jika itu merusak pusaran, tidak akan memperbaikinya!

4
Memberi +1 karena alasan yang tidak saya mengerti.
Jason C

40

Jawa

Variasi pemilih warna dalam 512x512. Kode elegan bukan , tapi saya suka gambar-gambar cantik:

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

Seperti yang tertulis, ini menghasilkan:

tidak acak

Jika Anda menjalankannya shuffle_block = true, mengacak warna di setiap blok 64x64:

blok shuffle

Lain, jika Anda menjalankannya shuffle_radius > 0, itu mengocok setiap piksel dengan piksel acak di shuffle_radiusdalam x / y. Setelah bermain dengan berbagai ukuran, saya suka jari-jari 32 piksel, karena mengaburkan garis tanpa memindahkan barang terlalu banyak:

masukkan deskripsi gambar di sini


3
ooh foto-foto ini adalah yang tercantik
sevenseacat

Ini benar-benar hebat 😍
Matius

37

Pengolahan

Saya baru saja memulai dengan C (setelah diprogram dalam bahasa lain) tetapi menemukan grafik dalam Visual C sulit untuk diikuti, jadi saya mengunduh program Pemrosesan ini yang digunakan oleh @ace.

Ini kode dan algoritma saya.

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

Algoritma

Mulailah dengan kotak 4x4 dari semua kombinasi yang mungkin dari 32 nilai hijau dan biru, dalam x, y. format, membuat 128x128 persegi Setiap persegi 4x4 memiliki 16 piksel, jadi buat gambar cermin di sebelahnya untuk memberikan 32 piksel dari setiap kombinasi yang mungkin dari hijau dan biru, per gambar di bawah ini.

(anehnya hijau penuh terlihat lebih terang daripada cyan penuh. Ini pasti ilusi optik. diklarifikasi dalam komentar)

Di kotak kiri, tambahkan nilai merah 0-15. Untuk kotak sebelah kanan, XOR nilai-nilai ini dengan 16, untuk membuat nilai-nilai 16-31.

masukkan deskripsi gambar di sini

Output 256x128

Ini memberikan output pada gambar atas di bawah ini.

Namun, setiap piksel berbeda dari gambar cerminnya hanya dalam bit yang paling signifikan dari nilai merah. Jadi, saya bisa menerapkan kondisi dengan variabel c, untuk membalikkan XOR, yang memiliki efek yang sama dengan bertukar dua piksel ini.

Contoh dari ini diberikan pada gambar di bawah ini (jika kita batalkan komentar pada baris kode yang saat ini dikomentari.)

masukkan deskripsi gambar di sini

512 x 512 - Penghargaan untuk Marylin karya Andy Warhol

Terinspirasi oleh jawaban Quincunx untuk pertanyaan ini dengan "seringai jahat" di lingkaran merah tangan-bebas, inilah versi saya dari gambar yang terkenal itu. Yang asli sebenarnya memiliki 25 Marylins berwarna dan 25 Marylins hitam & putih dan merupakan penghargaan Warhol untuk Marylin setelah kematiannya sebelum waktunya. Lihat http://en.wikipedia.org/wiki/Marilyn_Diptych

Saya berubah ke fungsi yang berbeda setelah menemukan bahwa Memproses membuat yang saya gunakan dalam 256x128 sebagai semitransparan. Yang baru buram.

Dan meskipun gambar tidak sepenuhnya algoritmik, saya lebih suka itu.

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

}

masukkan deskripsi gambar di sini

512x512 Senja di atas danau dengan gunung di kejauhan

Di sini, gambar yang sepenuhnya algoritmik. Saya telah bermain-main dengan mengubah warna yang saya modulasi dengan kondisi tersebut, tetapi saya baru saja kembali pada kesimpulan bahwa merah berfungsi paling baik. Mirip dengan gambar Marylin, saya menggambar gunung terlebih dahulu, lalu memilih kecerahan dari gambar itu untuk menimpa gambar RGB positif, sambil menyalin ke bagian negatif. Sedikit perbedaan adalah bahwa bagian bawah banyak gunung (karena semuanya digambar dengan ukuran yang sama) meluas di bawah area baca, sehingga area ini hanya dipangkas selama proses membaca (yang karenanya memberikan kesan yang diinginkan dari ukuran gunung yang berbeda. )

Dalam yang satu ini saya menggunakan sel 8x4 32 merah untuk positif, dan 32 merah sisanya untuk negatif.

Perhatikan frameRate perintah expicit (1) di akhir kode saya. Saya menemukan bahwa tanpa perintah ini, Pemrosesan akan menggunakan 100% dari satu inti CPU saya, meskipun sudah selesai menggambar. Sejauh yang saya tahu tidak ada fungsi Sleep, yang bisa Anda lakukan adalah mengurangi frekuensi polling.

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

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

masukkan deskripsi gambar di sini


Karena cyan tidak lengkap sama sekali. Ini (0,217,217). Semua 32 kombinasi hadir, hanya saja tidak diregangkan [0,255]. Sunting: Anda menggunakan langkah-langkah 7 tetapi saya tidak dapat menemukan ini dalam kode. Harus menjadi hal Pemrosesan.
Mark Jeronimus

@steveverrill Dalam Memproses, Anda dapat melakukannya save("filename.png")untuk menyimpan buffer bingkai saat ini ke gambar. Format gambar lain juga didukung. Ini akan menghemat kesulitan mengambil tangkapan layar. Gambar disimpan ke folder sketsa.
Jason C

@Jasonc terima kasih atas tipnya, saya yakin pasti ada caranya, tapi saya rasa saya tidak akan mengeditnya. Saya meninggalkan bingkai di sekitar gambar sebagian untuk memisahkannya (2 file untuk gambar kecil seperti itu berlebihan.) Saya ingin melakukan beberapa gambar dalam 512x512 (dan ada satu khususnya saya punya ide untuk) jadi saya akan mengunggahnya di jalan kamu menyarankan.
Level River St

1
@steveverrill Haha, Warhol adalah sentuhan yang bagus.
Jason C

@ Pemrosesan Zom-B tampaknya melakukan banyak hal yang (secara menjengkelkan) tidak disebutkan dalam dokumentasinya: tidak menggunakan 256 nilai saluran warna logis penuh dalam output fisiknya, memadukan warna ketika Anda tidak mau, menggunakan inti penuh dari CPU saya bahkan setelah selesai menggambar. Masih mudah untuk masuk dan Anda dapat mengatasi masalah ini begitu Anda tahu mereka ada di sana (kecuali yang pertama, saya belum memecahkannya ...)
Level River St

35

Saya baru saja mengatur semua warna 16-bit (5r, 6g, 5b) pada kurva Hilbert di JavaScript.

warna kurva hilbert

Gambar sebelumnya, (bukan kurva Hilbert):

kurva hilbert

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

Sunting : Ternyata ada bug dalam fungsi saya untuk menghitung kurva Hilbert dan itu salah; yaitu, r.x = (p.x & s) > 0; r.y = (p.y & s) > 0;diubah menjadir.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

Sunting 2: Fraktal lain:

sierpinsky

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


Bagus! Selamat datang di PPCG.
Jonathan Van Matre

Apa yang terlihat ketika berjalan melalui kubus warna juga kurva 3D Hilbert? Edit nm. seseorang melakukan hal itu.
Mark Jeronimus

35

C #: Optimasi kemiripan lokal berulang

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

Ide

Pertama kita mulai dengan acak acak:

masukkan deskripsi gambar di sini

Kemudian kami secara acak memilih dua piksel dan menukarnya. Jika ini tidak meningkatkan kesamaan piksel dengan tetangga mereka, kami menukar kembali dan mencoba lagi. Kami mengulangi proses ini berulang kali.

Setelah hanya beberapa generasi (5000) perbedaannya tidak begitu jelas ...

masukkan deskripsi gambar di sini

Tapi semakin lama berjalan (25000), ...

masukkan deskripsi gambar di sini

... semakin banyak pola tertentu mulai muncul (100000).

masukkan deskripsi gambar di sini

Menggunakan definisi yang berbeda untuk lingkungan , kita dapat mempengaruhi pola-pola ini dan apakah mereka stabil atau tidak. Ini Kerneladalah matriks yang mirip dengan yang digunakan untuk filter dalam pemrosesan gambar . Ini menentukan bobot masing-masing tetangga yang digunakan untuk perhitungan delta RGB.

Hasil

Berikut adalah beberapa hasil yang saya buat. Video menunjukkan proses berulang (1 frame == 1000 generasi), tetapi sayangnya kualitasnya bukan yang terbaik (vimeo, YouTube dll. Tidak mendukung dimensi sekecil itu). Saya nanti dapat mencoba membuat video dengan kualitas yang lebih baik.

0 1 0
1 X 1
0 1 0

185000 generasi:

masukkan deskripsi gambar di sini 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 generasi:

masukkan deskripsi gambar di sini 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 generasi:

masukkan deskripsi gambar di sini 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

Kernel ini menarik karena karena asimetrisnya, polanya tidak stabil dan seluruh gambar bergerak ke kanan seiring berjalannya waktu.

2331000 generasi:

masukkan deskripsi gambar di sini Video (01:10)


Hasil Besar (512x512)

Menggunakan kernel di atas dengan dimensi gambar yang lebih besar menciptakan pola lokal yang sama, membentang area total yang lebih besar. Gambar 512x512 membutuhkan stabilisasi antara 1 dan 2 juta generasi.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


OK, sekarang mari kita serius dan membuat pola yang lebih besar, kurang lokal dengan kernel radial 15x15:

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

Ini secara drastis meningkatkan waktu komputasi per generasi. 1,71 juta generasi dan 20 jam kemudian:

masukkan deskripsi gambar di sini


1
Butuh beberapa saat untuk sampai ke sana, tetapi hasil akhirnya cukup bagus.
Primo

Kebetulan yang menarik, saya punya artikel tentang topik yang sama: nayuki.io/page/simulated-annealing-demo
Nayuki

30

Jawa

Dengan beberapa variasi pada jawaban saya yang lain, kita bisa mendapatkan beberapa hasil yang sangat menarik.

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

Kode penting di sini:

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

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

});

Output (tangkapan layar):

masukkan deskripsi gambar di sini

Ubah komparator menjadi ini:

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

Dan kami mendapatkan ini:

masukkan deskripsi gambar di sini

Variasi lain:

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

masukkan deskripsi gambar di sini

Variasi lain (mengingatkan saya pada automata seluler):

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

masukkan deskripsi gambar di sini

Satu lagi variasi lain (favorit pribadi baru):

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

masukkan deskripsi gambar di sini

Itu terlihat sangat fraktal. XOR sangat cantik, terutama closeup:

masukkan deskripsi gambar di sini

Closeup lainnya:

masukkan deskripsi gambar di sini

Dan sekarang Segitiga Sierpinski, miring:

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

masukkan deskripsi gambar di sini


8
Gambar pertama terlihat seperti foto CPU atau memori
Nick T

@NickT Berikut ini adalah memori mati (menurut Gambar Google) untuk kontras: files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg
Justin

4
Benar, memori sangat tak berbentuk ... mungkin prosesor yang sangat multi-inti, kemudian: extremeetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg
Nick T

1
Saya sangat suka yang terakhir ini. Tampak sangat berkilau tetapi dengan struktur pengorganisasian yang mendasarinya. Saya ingin karpet yang ditenun seperti desain XOR!
Jonathan Van Matre

Ini sangat keren; mereka agak mengingatkan saya pada game arcade yang rusak atau sebuah nes.
Jason C

29

Jawa

Saya sebenarnya tidak yakin bagaimana cara membuat warna 15 atau 18-bit, jadi saya baru saja meninggalkan bit paling signifikan dari setiap byte saluran untuk membuat 2 ^ 18 warna 24-bit yang berbeda. Sebagian besar kebisingan dihilangkan dengan menyortir, tetapi penghapusan kebisingan yang efektif sepertinya membutuhkan perbandingan lebih dari dua elemen sekaligus seperti yang dilakukan oleh Comparator. Saya akan mencoba memanipulasi menggunakan kernel yang lebih besar, tetapi sementara itu, ini adalah yang terbaik yang bisa saya lakukan.

masukkan deskripsi gambar di sini

Klik untuk gambar HD # 2

Gambar beresolusi rendah # 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
Yang kedua benar-benar layak untuk memiliki versi 24
bit

Imgur telah memproses gambar selama sekitar setengah jam. Saya kira itu mungkin mencoba untuk mengompresnya. Lagi pula, saya menambahkan tautan: SSend.it/hj4ovh
John P

2
Ada masalah dengan unduhan.
SuperJedi224

28

Scala

Saya memesan semua warna dengan berjalan Hilbert Curve 3 dimensi melalui L-System . Saya kemudian berjalan piksel dalam gambar output sepanjang Kurva Hilbert 2-dimensi dan meletakkan semua warna.

Output 512 x 512:

masukkan deskripsi gambar di sini

Ini kodenya. Sebagian besar hanya mencakup logika dan matematika bergerak melalui tiga dimensi melalui pitch / roll / yaw. Saya yakin ada cara yang lebih baik untuk melakukan bagian itu, tapi oh well.

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

object AllColors {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

28

C #

Wow, hal yang sangat keren dalam tantangan ini. Saya mengambil bacokan ini di dalam C # dan menghasilkan gambar 4096x4096 dalam waktu sekitar 3 menit (i7 CPU) menggunakan setiap warna tunggal melalui logika Random Walk.

Ok, jadi untuk kodenya. Setelah merasa frustrasi dengan jam penelitian dan mencoba untuk menghasilkan setiap warna HSL tunggal menggunakan untuk loop dalam kode, saya memutuskan untuk membuat file flat untuk membaca warna HSL dari. Apa yang saya lakukan adalah membuat setiap warna RGB menjadi Daftar, lalu saya pesan dengan Hue, Luminosity, lalu Saturation. Kemudian saya menyimpan daftar ke file teks. ColorData hanyalah kelas kecil yang saya tulis yang menerima warna RGB dan juga menyimpan setara HSL. Kode ini adalah pemakan RAM BESAR. Digunakan sekitar 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;
    }


}

Dengan itu keluar dari jalan. Saya menulis sebuah kelas untuk mendapatkan warna berikutnya dari file yang dihasilkan. Ini memungkinkan Anda mengatur rona mulai dan rona akhir. Pada kenyataannya, itu bisa dan mungkin harus digeneralisasi ke dimensi mana pun file disortir terlebih dahulu. Saya juga menyadari bahwa untuk meningkatkan kinerja di sini, saya bisa saja meletakkan nilai RGB ke dalam file dan menjaga setiap baris pada panjang yang tetap. Dengan cara itu saya bisa dengan mudah menentukan byte offset daripada mengulang-ulang setiap baris sampai saya mencapai garis yang ingin saya mulai. Tapi itu bukan kinerja hit bagi saya. Tapi ini kelasnya

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

Jadi sekarang kita memiliki file warna, dan kita punya cara untuk membaca file, kita sekarang dapat benar-benar membuat gambar. Saya menggunakan kelas yang saya temukan untuk meningkatkan kinerja pengaturan piksel dalam bitmap, yang disebut LockBitmap. Sumber LockBitmap

Saya membuat kelas Vector2 kecil untuk menyimpan lokasi koordinat

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

Dan saya juga membuat kelas yang disebut SearchArea, yang sangat membantu untuk menemukan piksel tetangga. Anda menentukan piksel yang ingin Anda temukan tetangga, batas untuk mencari di dalamnya, dan ukuran "tetangga kuadrat" untuk mencari. Jadi jika ukurannya 3, itu artinya Anda mencari kotak 3x3, dengan piksel yang ditentukan tepat di tengah.

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

}

Inilah kelas yang benar-benar memilih tetangga berikutnya. Pada dasarnya ada 2 mode pencarian. A) Kotak penuh, B) hanya perimeter kotak. Ini adalah pengoptimalan yang saya lakukan untuk mencegah pencarian kotak penuh lagi setelah menyadari kotak penuh. DepthMap adalah optimasi lebih lanjut untuk mencegah pencarian kotak yang sama berulang-ulang. Namun, saya tidak sepenuhnya mengoptimalkan ini. Setiap panggilan ke GetNeighbors akan selalu melakukan pencarian persegi penuh terlebih dahulu. Saya tahu saya bisa mengoptimalkan ini hanya melakukan pencarian perimeter setelah menyelesaikan awal penuh. Saya hanya belum menyiasati optimasi itu, dan bahkan tanpa itu kodenya cukup cepat. Baris "kunci" yang dikomentari adalah karena saya menggunakan Parallel. ForEach pada satu titik, tetapi menyadari bahwa saya harus menulis lebih banyak kode daripada yang saya inginkan untuk lol itu.

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 bagus, jadi sekarang inilah kelas yang menciptakan gambar

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


}

Dan inilah contoh implementasi:

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

    }
}

Jika RandomWalkSegments = 1, maka itu pada dasarnya hanya berjalan di mana pun Anda katakan, dan dimulai pada warna pertama pertama dalam file.

Ini bukan kode terbersih yang akan saya akui, tetapi ini berjalan cukup cepat!

Output yang Dipotong

3 Jalan

128 Jalan

SUNTING:

Jadi saya sudah belajar tentang OpenGL dan Shader. Saya menghasilkan 4096x4096 menggunakan setiap warna sangat cepat pada GPU dengan 2 skrip shader sederhana. Hasilnya membosankan, tetapi seseorang mungkin menganggap ini menarik dan menghasilkan beberapa ide keren:

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
}

Sunting (10/15/16): Hanya ingin menunjukkan bukti konsep suatu algoritma genetika. Saya MASIH menjalankan kode ini 24 jam kemudian pada set 100x100 warna acak, tetapi sejauh ini hasilnya indah!masukkan deskripsi gambar di sini

Sunting (10/26/16): Saya telah menjalankan kode algoritma genetika selama 12 hari sekarang..dan masih mengoptimalkan output. Pada dasarnya konvergen ke beberapa minimum lokal tetapi tampaknya masih menemukan lebih banyak perbaikan:masukkan deskripsi gambar di sini

Sunting: 8/12/17 - Saya menulis algoritma berjalan acak baru - pada dasarnya Anda menentukan sejumlah "pejalan kaki", tetapi bukannya berjalan secara acak - mereka akan memilih walker lain secara acak dan juga menghindarinya (pilih piksel berikutnya yang tersedia terjauh dari sana) ) - atau berjalan ke arah mereka (pilih piksel berikutnya yang tersedia terdekat dengan mereka). Contoh keluaran grayscale ada di sini (saya akan melakukan render warna 4096x4096 penuh setelah saya memasang pewarnaan!):masukkan deskripsi gambar di sini


4
Sedikit terlambat, tapi selamat datang di PPCG! Ini adalah posting pertama yang bagus.
spaghetto

1
Terima kasih! Saya berharap dapat menyelesaikan lebih banyak tantangan! Saya telah melakukan lebih banyak pengodean gambar belakangan ini, ini hobi baru saya
applejacks01

Wow ini luar biasa; Saya senang saya kembali ke posting ini hari ini dan memeriksa semua hal selanjutnya.
Jason C

Terima kasih! Saya sebenarnya sedang melakukan beberapa algoritma genetika yang mengkode sekarang untuk menghasilkan gradien yang menarik. Pada dasarnya, ambil 10.000 warna, membentuk kisi 100x100. Untuk setiap piksel, dapatkan piksel tetangga. Untuk masing-masing, dapatkan jarak CIEDE2000. Ringkasnya. Jumlahkan hingga 10.000 piksel. Algoritma genetika mencoba mengurangi jumlah total itu. Ini lambat, tetapi untuk gambar 20x20 outputnya benar-benar menarik
applejacks01

Saya sangat menyukai hasil dari solusi ini.
r_alex_hall

22

Kanvas HTML5 + JavaScript

Saya menyebutnya randoGraph dan Anda dapat membuat sebanyak yang Anda inginkan di sini

Beberapa contoh:

Contoh 1

contoh 2

contoh 3

contoh 4

contoh 5

contoh 6

contoh 7

Misalnya di Firefox Anda dapat mengklik kanan di kanvas (saat selesai) dan menyimpannya sebagai gambar. Memproduksi gambar 4096x4096 adalah semacam masalah karena batas memori beberapa browser.

Idenya cukup sederhana tetapi setiap gambar unik. Pertama kita buat palet warna. Kemudian mulai dengan titik X kita memilih warna acak dari palet dan posisi untuk mereka (setiap kali kita memilih warna kita menghapusnya dari palet) dan kita merekam di mana kita meletakkannya untuk tidak menempatkan di posisi yang sama piksel berikutnya.

Untuk setiap piksel yang bersinggungan dengan itu kami membuat angka (X) dari kemungkinan warna dan kemudian kami memilih yang paling relevan dengan piksel itu. Ini berlangsung sampai gambar selesai.

Kode HTML

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

Dan JavaScript untuk 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;
};

Itu bagus tapi sepertinya jawaban C # dari fejesjoco . Apakah ini hanya kebetulan?
AL

1
Algoritma ada di sini dan siapa pun dapat membaca dan memahami yang sangat berbeda. Jawaban ini dipublikasikan setelah jawaban C # dari fejesjoco dinyatakan sebagai pemenang, memotivasi betapa bagus hasilnya. Lalu saya memikirkan pendekatan yang berbeda untuk memproses dan memilih warna tetangga, dan ini dia. Tentu saja kedua jawaban tersebut memiliki dasar yang sama, seperti distribusi warna yang digunakan secara merata di sepanjang spektrum yang terlihat, konsep warna yang relevan, dan titik awal, mungkin dengan meremehkan dasar tersebut seseorang dapat berpikir bahwa gambar yang dihasilkan memiliki kemiripan dalam beberapa kasus.
konstantinosX

Oke, saya minta maaf jika Anda pikir saya mengkritik jawaban Anda. Saya hanya ingin tahu apakah Anda terinspirasi oleh jawaban fejesjoco karena hasil yang dihasilkan terlihat serupa.
AL

1
"Mendefinisikan metode kelas di dalam konstruktor alih-alih menggunakan rantai prototipe benar-benar tidak efisien, terutama jika kelas tersebut digunakan beberapa kali." Komentar Patrick Roberts yang sangat menarik. Apakah Anda memiliki referensi dengan contoh yang memvalidasi itu? , Saya dengan tulus ingin tahu apakah klaim ini memiliki dasar (untuk berhenti menggunakan itu), dan apa itu.
konstantinosX

2
Mengenai penggunaan prototipe: ia bekerja dengan cara yang hampir sama dengan metode statis. Ketika Anda memiliki fungsi yang didefinisikan dalam objek literal, setiap objek baru yang Anda buat juga harus membuat salinan fungsi tersebut, dan menyimpannya dengan instance objek tersebut (jadi 16 juta objek warna berarti 16 juta salinan dari fungsi yang sama persis di ingatan). Sebagai perbandingan, menggunakan prototipe hanya akan membuatnya sekali, untuk dikaitkan dengan "kelas" daripada objek. Ini memiliki manfaat memori yang jelas serta manfaat kecepatan potensial.
Mwr247

20

Python

Jadi inilah solusi saya dengan python, butuh hampir satu jam untuk membuatnya, jadi mungkin ada beberapa optimasi yang harus dilakukan:

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

Berikut adalah beberapa contoh output:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


1
Sepertinya beberapa bentuk gelombang suara gila
Mark Jeronimus

19

Jawa

Saya memutuskan untuk mencoba tantangan ini. Saya terinspirasi oleh jawaban ini untuk kode golf lain. Program saya menghasilkan gambar yang lebih buruk, tetapi mereka memiliki semua warna.

Juga, golf kode waktu pertamaku. :)

(4k gambar terlalu besar untuk kecepatan unggahan kecil saya, saya mencoba mengunggah satu tetapi setelah satu jam itu tidak diunggah. Anda dapat menghasilkan sendiri.)

Merapatkan:

Menghasilkan gambar dalam 70 detik pada mesin saya, membutuhkan sekitar 1,5GB memori saat menghasilkan

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]

Hasil (2x):

256x128 2x

Gambar 256x128 asli

Sunting:

dengan mengganti Log10 [i] dengan Log10 [i] / 5 Anda mendapatkan: masukkan deskripsi gambar di sini

Kode di atas terkait dengan anil simulasi. Terlihat dengan cara ini, gambar kedua dibuat dengan "suhu" yang lebih tinggi dalam 10 ^ 6 langkah pertama. Semakin tinggi "suhu" menyebabkan permutasi lebih banyak di antara piksel, sedangkan pada gambar pertama struktur gambar yang diurutkan masih sedikit terlihat.


17

JavaScript

Masih seorang mahasiswa dan pertama kali saya memposting jadi kode saya mungkin berantakan dan saya tidak 100% yakin bahwa gambar saya memiliki semua warna yang dibutuhkan tetapi saya sangat senang dengan hasil saya jadi saya pikir saya akan mempostingnya.

Saya tahu kontes telah berakhir, tetapi saya sangat menyukai hasil ini dan saya selalu menyukai tampilan labirin yang dihasilkan secara berulang, jadi saya pikir mungkin akan keren untuk melihat seperti apa seseorang jika menempatkan piksel berwarna. Jadi saya mulai dengan menghasilkan semua warna dalam array kemudian saya melakukan backtracking rekursif sambil memunculkan warna dari array.

Inilah JSFiddle saya 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);
    }
}

Gambar 256x128, warna disortir merah-> hijau-> biru
Warna RGB Disortir

Gambar 256x128, warna disortir biru-> hijau-> merah
BGR Sorted Colors

Gambar 256x128, warna diurutkan hue-> luminance-> saturation
HLS Sorted Colors

Dan akhirnya GIF yang dihasilkan
Warna Labirin GIF


Warna Anda terpotong di daerah paling terang, menyebabkan duplikat. Ubah r * Math.ceil(255 / (colorSteps - 1)ke r * Math.floor(255 / (colorSteps - 1), atau bahkan lebih baik: r * 255 / (colorSteps - 1)(belum diuji, karena Anda tidak menyediakan jsfiddle)
Mark Jeronimus

Aduh, ya aku punya perasaan yang akan menyebabkan masalah, semoga sudah diperbaiki sekarang, dan maaf tentang kurangnya jsfiddle (saya tidak tahu itu ada!) Terima kasih!
Kuligoawesome

Saya suka kekacauan yang dipesan / keluaran yang terlihat berisik dari ini dan solusi lain yang menghasilkan keluaran yang serupa.
r_alex_hall

17

C #

Jadi saya mulai mengerjakan ini hanya sebagai latihan yang menyenangkan dan berakhir dengan hasil yang setidaknya bagi saya terlihat cukup rapi. Perbedaan utama dalam solusi saya untuk (setidaknya) sebagian besar yang lain adalah bahwa saya menghasilkan persis jumlah warna yang diperlukan untuk memulai dan secara merata membuat generasi keluar dari putih murni menjadi hitam murni. Saya juga mengatur warna yang bekerja dalam spiral ke dalam dan memilih warna berikutnya berdasarkan rata-rata perbedaan warna antara semua tetangga yang telah ditetapkan.

Berikut ini adalah contoh kecil keluaran yang telah saya hasilkan sejauh ini, saya sedang mengerjakan render 4k, tetapi saya berharap ini akan selesai dalam sehari.

Berikut adalah contoh output spesifikasi pada 256x128:

Spesifikasi Render

Beberapa gambar yang lebih besar dengan waktu render yang masih masuk akal:

Render pada 360 x 240

Jalankan kedua pada 360 x 240 menghasilkan gambar yang jauh lebih halus

Render # 2 pada 360 x 240

Setelah meningkatkan kinerja, saya dapat menjalankan render HD yang membutuhkan waktu 2 hari, saya belum menyerah pada 4k namun butuh waktu berminggu-minggu.

Render HD

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

Jika ada yang memiliki pemikiran tentang bagaimana meningkatkan kinerja algoritma pemilihan warna, beri tahu saya, karena 360-240 render memerlukan waktu sekitar 15 menit. Saya tidak percaya itu bisa diparalelkan tetapi saya bertanya-tanya apakah akan ada cara yang lebih cepat untuk mendapatkan perbedaan warna terendah.
Phaeze

Bagaimana gambar 360 * 240 merupakan 'semua warna'? Bagaimana Anda menghasilkan cbrt (360 * 240) = 44.208377983684639269357874002958 warna per komponen?
Mark Jeronimus

Bahasa apa ini? Mengacak daftar jenis dan Acak adalah ide yang buruk, karena tergantung pada algoritma dan implementasinya, hal itu dapat menyebabkan hasil yang bias atau pengecualian yang menyatakan bahwa "Comparison method violates its general contract!": karena kontrak menyatakan itu (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. Untuk mengacak daftar, gunakan beberapa metode Acak yang disediakan. ( colors.Shuffle()?)
Mark Jeronimus

@ MarkJeronimus Saya akui saya melewatkan spesifikasi tentang gambar 256x128, saya akan melakukan rendering sederhana menggunakan ukuran-ukuran itu, saya memfokuskan pada setiap pixel adalah aspek warna yang unik dari tantangan dan renders yang lebih besar seperti kiriman lainnya.
Phaeze

@ MarkJeronimus Saya menyadari jenis acak buruk, sebenarnya ada komentar yang mengatakan banyak. Ini hanya sisa dari pendekatan lain yang saya mulai ambil dan saya memprioritaskan menyelesaikan rendering besar karena mereka membutuhkan waktu yang sangat lama.
Phaeze

16

Pergi

Inilah satu lagi dari saya, saya pikir itu memberikan hasil yang lebih menarik:

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

Dimulai dengan pola yang sama dengan gif di jawaban saya yang lain . Kemudian, ia mengocoknya menjadi ini:

hanya kebisingan

Semakin banyak iterasi saya menjalankan algoritma perbandingan tetangga yang agak tidak terinspirasi, semakin jelas pola pelangi menjadi.

Inilah 16384:

pelangi yang sangat bising pada iterasi 16384

Dan 65536:

masukkan deskripsi gambar di sini


6
+1 Saya suka suatu pola muncul dari itu; Anda harus membuat animasi itu!
Jason C

16

Gambar-gambar ini adalah "Langton's Rainbow". Mereka digambar agak sederhana: saat semut Langton bergerak, sebuah warna digambar pada setiap piksel saat pertama kali piksel dilihat. Warna untuk menggambar berikutnya kemudian hanya bertambah 1, memastikan 2 ^ 15 warna digunakan, satu untuk setiap piksel.

EDIT: Saya membuat versi yang menghasilkan 4096X4096 gambar, menggunakan 2 ^ 24 warna. Warnanya juga 'dipantulkan' sehingga membuat gradien yang bagus dan halus. Saya hanya akan memberikan tautan kepada mereka karena ukurannya sangat besar (> 28 MB)

Langton's Rainbow besar, memerintah LR

Langton's Rainbow besar, memerintah LLRR

// Akhir pengeditan.

Ini untuk set aturan LR klasik:

Langton's Rainbow LR

Inilah LLRR:

Langton's Rainbow LLRR

Akhirnya, yang ini menggunakan aturan LRRRRRLLR:

Langton's Rainbow LRRRRRLLR

Ditulis dalam C ++, menggunakan CImg untuk grafik. Saya juga harus menyebutkan bagaimana warna dipilih: Pertama, saya menggunakan pendek unsigned untuk berisi data warna RGB. Setiap kali sel pertama kali dilihat, saya menggeser bit dengan kelipatan 5, DAN 31, kemudian dikalikan dengan 8. Kemudian warna pendek yang tidak ditandai bertambah dengan 1. Ini menghasilkan nilai dari 0 hingga 248 paling banyak. Namun, saya mengurangi nilai ini dari 255 dalam komponen merah dan biru, oleh karena itu R dan B adalah dalam kelipatan 8, mulai dari 255, turun ke 7:

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

Namun, ini tidak berlaku untuk komponen hijau, yang dalam kelipatan 8 dari 0 hingga 248. Dalam hal apa pun, setiap piksel harus berisi warna yang unik.

Bagaimanapun, kode sumber di bawah ini:

#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
Selamat datang, dan bergabunglah dengan klub. Mungkin menarik untuk mencoba turmites lain dari code.google.com/p/ruletablerepository/wiki/… (Saya berpartisipasi dalam hal itu)
Mark Jeronimus

3
Tautan gambar mati karena Dropbox membunuh Folder Publik.
user2428118

15

Rubi

Saya memutuskan untuk melanjutkan dan membuat PNG dari awal, karena saya pikir itu akan menarik. Kode ini secara harfiah menghasilkan mentah data biner ke dalam file.

Saya melakukan versi 512x512. (Algoritma ini agak tidak menarik.) Itu selesai dalam waktu sekitar 3 detik pada mesin saya.

require 'zlib'

class RBPNG
  def initialize
    # PNG header
    @data = [137, 80, 78, 71, 13, 10, 26, 10].pack 'C*'
  end

  def chunk name, data = ''
    @data += [data.length].pack 'N'
    @data += name
    @data += data
    @data += [Zlib::crc32(name + data)].pack 'N'
  end

  def IHDR opts = {}
    opts = {bit_depth: 8, color_type: 6, compression: 0, filter: 0, interlace: 0}.merge opts
    raise 'IHDR - Missing width param' if !opts[:width]
    raise 'IHDR - Missing height param' if !opts[:height]

    self.chunk 'IHDR', %w[width height].map {|x| [opts[x.to_sym]].pack 'N'}.join +
                       %w[bit_depth color_type compression filter interlace].map {|x| [opts[x.to_sym]].pack 'C'}.join
  end

  def IDAT data; self.chunk 'IDAT', Zlib.deflate(data); end
  def IEND; self.chunk 'IEND'; end
  def write filename; IO.binwrite filename, @data; end
end

class Color
  attr_accessor :r, :g, :b, :a

  def initialize r = 0, g = 0, b = 0, a = 255
    if r.is_a? Array
      @r, @g, @b, @a = @r
      @a = 255 if !@a
    else
      @r = r
      @g = g
      @b = b
      @a = a
    end
  end

  def hex; '%02X' * 4 % [@r, @g, @b, @a]; end
  def rgbhex; '%02X' * 3 % [@r, @g, @b]; end
end

img = RBPNG.new
img.IHDR({width: 512, height: 512, color_type: 2})
#img.IDAT ['00000000FFFFFF00FFFFFF000000'].pack 'H*'
r = g = b = 0
data = Array.new(512){ Array.new(512){
  c = Color.new r, g, b
  r += 4
  if r == 256
    r = 0
    g += 4
    if g == 256
      g = 0
      b += 4
    end
  end
  c
} }
img.IDAT [data.map {|x| '00' + x.map(&:rgbhex).join }.join].pack 'H*'
img.IEND
img.write 'all_colors.png'

Output (in all_colors.png) (klik salah satu gambar ini untuk memperbesarnya):

Keluaran

Output gradient-ish yang lebih menarik (dengan mengubah baris ke-4 menjadi terakhir }.shuffle }):

Keluaran 2

Dan dengan mengubahnya }.shuffle }.shuffle, Anda mendapatkan garis warna gila:

Keluaran 3


Itu keren sekali. Apakah ada cara agar Anda bisa membuatnya lebih cantik? Mungkin mengacak piksel? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian Ok, diedit
Doorknob

Jauh lebih baik!!!!!!!

1
Apa yang akan terjadi jika Anda mengubah baris ke-4 menjadi baris terakhir }.shuffle }.shuffle }.shuffle?
John Odom

6
@ John Erm, kesalahan sintaks, mungkin?
Gagang Pintu

14

Python

plasma

Menggunakan python untuk mengurutkan warna berdasarkan luminance, menghasilkan pola luminance dan memilih warna yang paling tepat. Pixel diulang dalam urutan acak sehingga pencahayaan yang kurang cocok yang secara alami terjadi ketika daftar warna yang tersedia semakin kecil tersebar secara merata di seluruh gambar.

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

Jawa

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

Saya memilih 4096 dengan 4096 karena saya tidak tahu bagaimana mendapatkan semua warna tanpa melakukannya.

Keluaran:

Terlalu besar untuk muat di sini. Ini adalah tangkapan layar:

masukkan deskripsi gambar di sini

Dengan sedikit perubahan, kita bisa mendapatkan gambar yang lebih indah:

Tambahkan Collections.shuffle(points, new Random(0));antara menghasilkan poin dan melakukan warna:

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

masukkan deskripsi gambar di sini

Merapatkan:

masukkan deskripsi gambar di sini


29
Anda menyebut gumpalan abu-abu besar "indah"?
Gagang Pintu

22
@ Doorknob Ya. Saya menyebutnya sangat indah. Saya merasa luar biasa bahwa semua warna dapat diatur menjadi gumpalan abu-abu besar. Saya menemukan gumpalan lebih menarik ketika saya memperbesar. Dengan sedikit lebih detail, Anda dapat melihat bagaimana rng Java tidak acak. Ketika kita memperbesar lebih banyak lagi, seperti tangkapan layar kedua, menjadi jelas berapa banyak warna pada benda itu. Ketika saya memperbesar lebih jauh, sepertinya program Piet.
Justin

Saya mendapatkan warna dalam versi yang lebih kecil dengan menjatuhkan bit yang lebih rendah.
Mark Jeronimus

Ya, bit yang lebih rendah untuk r, gdan bsecara terpisah, tetapi saya berurusan dengan mereka sebagai satu nomor.
Justin

Saya melihat Anda telah menemukan jawaban ajaib dalam jawaban Anda berikutnya. Pada topik, mungkin menarik untuk bereksperimen dengan Randomsubkelas Anda sendiri yang menghasilkan angka acak yang bahkan kurang ideal.
Mark Jeronimus

13

C ++ 11

( Pembaruan: hanya setelah itu saya melihat bahwa pendekatan yang sama telah dicoba --- dengan lebih sabar sehubungan dengan jumlah iterasi.)

Untuk setiap piksel, saya menetapkan satu set piksel tetangga. Saya mendefinisikan perbedaan antara dua piksel sebagai jumlah kuadrat dari perbedaan R / G / B mereka. Hukuman dari piksel yang diberikan adalah jumlah dari perbedaan antara piksel dan tetangganya.

Sekarang, saya pertama kali menghasilkan permutasi acak, kemudian mulai memilih pasangan piksel acak. Jika menukar kedua piksel mengurangi jumlah total denda dari semua piksel, swap akan dilewati. Saya ulangi ini selama jutaan kali.

Outputnya dalam format PPM, yang saya konversi menjadi PNG menggunakan utilitas standar.

Sumber:

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

Memvariasikan langkah tetangga memberikan hasil yang berbeda. Ini dapat di-tweak dalam fungsi Image :: pixelNeighbors (). Kode mencakup contoh untuk empat opsi: (lihat sumber)

contoh 01 contoh 02 contoh 03 contoh 04

Sunting: contoh lain yang mirip dengan yang keempat di atas tetapi dengan kernel yang lebih besar dan lebih banyak iterasi:

contoh 05

Satu lagi: menggunakan

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

dan sepuluh juta iterasi, saya mendapatkan ini:

contoh 06


11

Bukan kode yang paling elegan, tetapi menarik dalam dua hal: Menghitung jumlah warna dari dimensi (selama produk dimensi adalah kekuatan dua), dan melakukan hal-hal ruang warna trippy:

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

Beberapa variasi menarik dapat dimiliki hanya dengan mengubah klausa OrderBy:

x:

masukkan deskripsi gambar di sini

y:

masukkan deskripsi gambar di sini

z:

masukkan deskripsi gambar di sini

Y:

masukkan deskripsi gambar di sini

Saya berharap saya bisa mencari tahu apa yang menyebabkan garis aneh di tiga yang pertama


2
Garis-garis aneh itu mungkin bias dari semacam atau metode pencarian (pencarian biner / quicksort?)
Mark Jeronimus

Saya sebenarnya sangat suka dialog di sini.
Jason C

11

Jawa

Ini adalah ide yang jauh lebih baik. Ini adalah beberapa kode Java yang sangat pendek; metode utama hanya 13 baris:

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

Menghasilkan blok "pemetik warna". Pada dasarnya, di blok pertama,, di blok r=0kedua r=1, dll. Di setiap blok, gkenaikan terhadap x, dan bsehubungan dengany .

Saya sangat suka operator bitwise. Biarkan saya memecah setRGBpernyataan:

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

Sebagai hasil dari operator bitwise, ini hanya membutuhkan waktu 7 detik untuk menyelesaikannya. Jika r & 15diganti denganr % 16 , dibutuhkan 9 detik.

Saya memilih 4096 x 4096

Output (tangkapan layar, terlalu besar jika tidak):

masukkan deskripsi gambar di sini

Keluaran dengan seringai jahat ditarik padanya oleh lingkaran-tangan-bebas:

masukkan deskripsi gambar di sini


2
Tautan ke dokumen asli sehingga saya dapat memverifikasi validitas (hitung warna)
Mark Jeronimus

2
Lol! Saya lupa saya bisa menjalankan kode Java. Gambar pertama berlalu, dan saya tidak dapat mereproduksi gambar kedua (lol) ☺
Mark Jeronimus

16
Semua lingkaran tangan bebas memiliki warna yang sama, didiskualifikasi. : P
Nick T

3
@Quincunx +1 jika Anda dapat menggambar wajah yang menakutkan dan tetap memenuhi persyaratan warna!
Jason C

2
@JasonC Lihat jawaban saya. Penghargaan diberikan kepada Quincunx untuk inspirasi.
Level River St
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.