Images de toutes les couleurs


433

Comme pour les images sur allrgb.com , créez des images dans lesquelles chaque pixel a une couleur unique (aucune couleur n'est utilisée deux fois ni aucune couleur manquante).

Donnez un programme qui génère une telle image, avec une capture d'écran ou un fichier de la sortie (télécharger au format PNG).

  • Créez l'image purement algorithmique.
  • L'image doit être 256 × 128 (ou une grille qui peut être capturée et sauvegardée à 256 × 128)
  • Utilisez toutes les couleurs 15 bits *
  • Aucune entrée externe autorisée (également aucune requête Web, URL ou base de données)
  • Aucune image intégrée autorisée (le code source correspondant à une image convient, par exemple, Piet )
  • Le dithering est autorisé
  • Ce n'est pas un concours de codes courts, même s'il pourrait vous faire gagner des votes.
  • Si vous êtes vraiment prêt pour un défi, faites 512 × 512, 2048 × 1024 ou 4096 × 4096 (par incréments de 3 bits).

La notation est par vote. Votez pour les plus belles images réalisées avec le code le plus élégant et / ou l’algorithme le plus intéressant.

Les algorithmes à deux étapes, dans lesquels vous générez d'abord une belle image, puis adaptez tous les pixels à l'une des couleurs disponibles, sont bien entendu autorisés, mais ne vous rapportent pas des points d'élégance.

* Les couleurs 15 bits sont les 32 768 couleurs qui peuvent être obtenues en mélangeant 32 rouges, 32 verts et 32 ​​bleus, tous par incréments et plages égales. Exemple: dans les images 24 bits (8 bits par canal), la plage par canal est 0..255 (ou 0..224), divisez-la donc en 32 nuances équidistantes.

Pour être très clair, le tableau de pixels de l'image doit être une permutation, car toutes les images possibles ont les mêmes couleurs, mais à des emplacements de pixels différents. Je vais donner ici une permutation triviale, qui n'est pas belle du tout:

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

entrez la description de l'image ici

Gagnant

Parce que les 7 jours sont passés, je déclare gagnant

Cependant, en aucun cas, pensez que c'est fini. Tous les lecteurs et moi-même souhaitons toujours des designs plus impressionnants. Ne pas arrêter de créer.

Gagnant: fejesjoco avec 231 votes


8
Quand vous dites "le dither est autorisé", que voulez-vous dire? Est-ce une exception à la règle "chaque pixel est une couleur unique"? Si non, qu'autorisez-vous qui était autrement interdit?
Peter Taylor

1
Cela signifie que vous pouvez placer des couleurs dans un motif. Ainsi, lorsqu'elles sont visualisées à l'œil nu, elles se fondent dans une couleur différente. Par exemple, voir l'image "clairement tous les RVB" sur la page allRGB, et beaucoup d'autres là.
Mark Jeronimus

8
En fait, je trouve votre exemple de permutation trivial très agréable à regarder.
Jason C

2
@ Zom-B Man, j'adore ce billet. Merci!
Jason C

7
Beaux résultats / réponses!
EthanB

Réponses:


534

C #

Je mets un pixel aléatoire au milieu, puis commence à mettre des pixels aléatoires dans un quartier qui leur ressemble le plus. Deux modes sont pris en charge: avec une sélection minimale, un seul pixel voisin est considéré à la fois; avec une sélection moyenne, toutes (1.,8) sont moyennées. La sélection minimale est quelque peu bruyante, la sélection moyenne est bien sûr plus floue, mais les deux ressemblent à des peintures. Après quelques modifications, voici la version actuelle, quelque peu optimisée (elle utilise même un traitement parallèle!):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;

class Program
{
    // algorithm settings, feel free to mess with it
    const bool AVERAGE = false;
    const int NUMCOLORS = 32;
    const int WIDTH = 256;
    const int HEIGHT = 128;
    const int STARTX = 128;
    const int STARTY = 64;

    // represent a coordinate
    struct XY
    {
        public int x, y;
        public XY(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public override int GetHashCode()
        {
            return x ^ y;
        }
        public override bool Equals(object obj)
        {
            var that = (XY)obj;
            return this.x == that.x && this.y == that.y;
        }
    }

    // gets the difference between two colors
    static int coldiff(Color c1, Color c2)
    {
        var r = c1.R - c2.R;
        var g = c1.G - c2.G;
        var b = c1.B - c2.B;
        return r * r + g * g + b * b;
    }

    // gets the neighbors (3..8) of the given coordinate
    static List<XY> getneighbors(XY xy)
    {
        var ret = new List<XY>(8);
        for (var dy = -1; dy <= 1; dy++)
        {
            if (xy.y + dy == -1 || xy.y + dy == HEIGHT)
                continue;
            for (var dx = -1; dx <= 1; dx++)
            {
                if (xy.x + dx == -1 || xy.x + dx == WIDTH)
                    continue;
                ret.Add(new XY(xy.x + dx, xy.y + dy));
            }
        }
        return ret;
    }

    // calculates how well a color fits at the given coordinates
    static int calcdiff(Color[,] pixels, XY xy, Color c)
    {
        // get the diffs for each neighbor separately
        var diffs = new List<int>(8);
        foreach (var nxy in getneighbors(xy))
        {
            var nc = pixels[nxy.y, nxy.x];
            if (!nc.IsEmpty)
                diffs.Add(coldiff(nc, c));
        }

        // average or minimum selection
        if (AVERAGE)
            return (int)diffs.Average();
        else
            return diffs.Min();
    }

    static void Main(string[] args)
    {
        // create every color once and randomize the order
        var colors = new List<Color>();
        for (var r = 0; r < NUMCOLORS; r++)
            for (var g = 0; g < NUMCOLORS; g++)
                for (var b = 0; b < NUMCOLORS; b++)
                    colors.Add(Color.FromArgb(r * 255 / (NUMCOLORS - 1), g * 255 / (NUMCOLORS - 1), b * 255 / (NUMCOLORS - 1)));
        var rnd = new Random();
        colors.Sort(new Comparison<Color>((c1, c2) => rnd.Next(3) - 1));

        // temporary place where we work (faster than all that many GetPixel calls)
        var pixels = new Color[HEIGHT, WIDTH];
        Trace.Assert(pixels.Length == colors.Count);

        // constantly changing list of available coordinates (empty pixels which have non-empty neighbors)
        var available = new HashSet<XY>();

        // calculate the checkpoints in advance
        var checkpoints = Enumerable.Range(1, 10).ToDictionary(i => i * colors.Count / 10 - 1, i => i - 1);

        // loop through all colors that we want to place
        for (var i = 0; i < colors.Count; i++)
        {
            if (i % 256 == 0)
                Console.WriteLine("{0:P}, queue size {1}", (double)i / WIDTH / HEIGHT, available.Count);

            XY bestxy;
            if (available.Count == 0)
            {
                // use the starting point
                bestxy = new XY(STARTX, STARTY);
            }
            else
            {
                // find the best place from the list of available coordinates
                // uses parallel processing, this is the most expensive step
                bestxy = available.AsParallel().OrderBy(xy => calcdiff(pixels, xy, colors[i])).First();
            }

            // put the pixel where it belongs
            Trace.Assert(pixels[bestxy.y, bestxy.x].IsEmpty);
            pixels[bestxy.y, bestxy.x] = colors[i];

            // adjust the available list
            available.Remove(bestxy);
            foreach (var nxy in getneighbors(bestxy))
                if (pixels[nxy.y, nxy.x].IsEmpty)
                    available.Add(nxy);

            // save a checkpoint
            int chkidx;
            if (checkpoints.TryGetValue(i, out chkidx))
            {
                var img = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format24bppRgb);
                for (var y = 0; y < HEIGHT; y++)
                {
                    for (var x = 0; x < WIDTH; x++)
                    {
                        img.SetPixel(x, y, pixels[y, x]);
                    }
                }
                img.Save("result" + chkidx + ".png");
            }
        }

        Trace.Assert(available.Count == 0);
    }
}

256x128 pixels, commençant au milieu, sélection minimale:

256x128 pixels, en partant du coin supérieur gauche, sélection minimale:

256x128 pixels, commençant au milieu, sélection moyenne:

Voici deux animgifs de 10 images qui montrent comment fonctionnent les sélections minimale et moyenne (félicitations au format gif pour pouvoir l'afficher en 256 couleurs uniquement):

Le mode de sélection minimale croît avec un petit front d'onde, comme une goutte, remplissant tous les pixels au fur et à mesure. Dans le mode moyen, cependant, lorsque deux branches de couleurs différentes commencent à se développer côte à côte, il y aura un petit espace noir, car rien ne sera suffisamment proche de deux couleurs différentes. En raison de ces lacunes, le front d'onde sera d'un ordre de grandeur supérieur, donc l'algorithme sera beaucoup plus lent. Mais c'est bien parce que cela ressemble à un corail en croissance. Si je laisse tomber le mode moyen, il pourrait être rendu un peu plus rapide car chaque nouvelle couleur est comparée à chaque pixel existant environ 2-3 fois. Je ne vois pas d'autre moyen de l'optimiser, je pense que c'est assez bon tel quel.

Et la grande attraction, voici un rendu de 512x512 pixels, début moyen, sélection minimum:

Je ne peux tout simplement pas arrêter de jouer avec ça! Dans le code ci-dessus, les couleurs sont triées de manière aléatoire. Si nous ne trions pas du tout ou que nous ne trions pas par hue ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue())), nous obtenons ces informations (sélection de départ moyen et de sélection minimale):

Une autre combinaison, où la forme corallienne est conservée jusqu’à la fin: teinte ordonnée avec une sélection moyenne, avec un animgif de 30 images:

MISE À JOUR: C'EST PRÊT !!!

Vous vouliez salut-résolution, je voulais salut-résolution, vous étiez impatient, j'ai à peine dormi. Maintenant, je suis heureux d’annoncer que la qualité de la production est enfin prête. Et je le publie avec un big bang, une superbe vidéo YouTube 1080p! Cliquez ici pour la vidéo , faisons en sorte qu'il soit viral de promouvoir le style geek. Je publie également des articles sur mon blog à l' adresse http://joco.name/ . Un article technique contenant tous les détails intéressants, les optimisations, la façon dont j'ai réalisé la vidéo, etc. sera publié . Enfin, je partage le code source. code sous GPL. C'est devenu énorme alors un bon hébergement est le meilleur endroit pour cela, je ne modifierai plus la partie ci-dessus de ma réponse. Assurez-vous de compiler en mode release! Le programme s'adapte bien à de nombreux cœurs de processeur. Un rendu 4Kx4K nécessite environ 2 à 3 Go de RAM.

Je peux maintenant rendre des images énormes en 5-10 heures. J'ai déjà quelques rendus 4Kx4K, je les posterai plus tard. Le programme a beaucoup progressé, il y a eu d'innombrables optimisations. Je l'ai également rendu convivial pour que tout le monde puisse l'utiliser facilement, il dispose d'une belle ligne de commande. Le programme est également aléatoire de manière déterministe, ce qui signifie que vous pouvez utiliser une graine aléatoire et générer la même image à chaque fois.

Voici quelques grands rendus.

Mon 512 préféré:


(source: joco.name )

Les 2048 qui apparaissent dans ma vidéo :


(source: joco.name )


(source: joco.name )


(source: joco.name )


(source: joco.name )

Les premiers 4096 rendus (TODO: ils sont en train d'être téléchargés et mon site Web ne peut pas gérer le trafic important, ils sont donc temporairement déplacés):


(source: joco.name )


(source: joco.name )


(source: joco.name )


(source: joco.name )


25
Maintenant c'est cool!
Jaa-c

5
Très sympa :-D Maintenant fais-en de plus gros!
ossifrage

20
Tu es un vrai artiste! :)
AL

10
Combien pour une impression?
Primo

16
Je travaille sur des rendus énormes et une vidéo 1080p. Ça va prendre des heures ou des jours. J'espère que quelqu'un sera capable de créer une impression à partir d'un gros rendu. Ou même un t-shirt: code d'un côté, image de l'autre. Quelqu'un peut-il arranger ça?
fejesjoco

248

En traitement

Mise à jour!4096x4096 images!

J'ai fusionné mon deuxième article dans celui-ci en combinant les deux programmes.

Une collection complète d'images sélectionnées peut être trouvée ici, sur Dropbox .(Remarque: DropBox ne peut pas générer d’aperçus pour les images 4096x4096; il suffit de cliquer dessus puis de cliquer sur "Télécharger").

Si vous ne regardez qu'un seul coup d' oeil sur celui-ci ! Ici, il est réduit (et beaucoup plus ci-dessous), original 2048x1024:

enter image description here

Ce programme fonctionne en parcourant des chemins à partir de points sélectionnés de manière aléatoire dans le cube de couleur, puis en les dessinant dans des chemins sélectionnés de manière aléatoire dans l'image. Il y a beaucoup de possibilités. Les options configurables sont:

  • Longueur maximale du chemin de cube de couleur.
  • Pas maximum à parcourir dans le cube de couleur (des valeurs plus grandes entraînent une variance plus importante mais minimisent le nombre de petits trajets vers la fin lorsque la situation devient trop étroite).
  • Mosaïque de l'image.
  • Il existe actuellement deux modes de chemin d'image:
    • Mode 1 (mode de ce message d'origine): recherche un bloc de pixels inutilisés dans l'image et effectue le rendu sur ce bloc. Les blocs peuvent être situés au hasard ou ordonnés de gauche à droite.
    • Mode 2 (le mode de mon deuxième message que j'ai fusionné dans celui-ci): sélectionne un point de départ aléatoire dans l'image et parcourt un chemin à travers des pixels inutilisés; peut marcher autour des pixels utilisés. Options pour ce mode:
      • Ensemble de directions pour marcher (orthogonal, diagonal ou les deux).
      • S'il faut ou non changer la direction (actuellement dans le sens des aiguilles d'une montre mais le code est flexible) après chaque étape, ou pour changer de direction uniquement lorsqu'un pixel occupé est rencontré.
      • Option permettant de mélanger l'ordre des changements de direction (au lieu de dans le sens des aiguilles d'une montre).

Cela fonctionne pour toutes les tailles jusqu'à 4096x4096.

Le schéma complet de traitement peut être trouvé ici: Tracer.zip

J'ai collé tous les fichiers dans le même bloc de code ci-dessous juste pour économiser de l'espace (même dans un seul fichier, c'est toujours une esquisse valide). Si vous souhaitez utiliser l'un des préréglages, modifiez l'index dans l' gPresetaffectation. Si vous l’exécutez dans Traitement, vous pouvez appuyer sur rpendant l’exécution pour générer une nouvelle image.

  • Mise à jour 1: Code optimisé pour suivre la première couleur / pixel inutilisée et ne pas rechercher par rapport aux pixels connus; réduction du temps de génération de 2048x1024 de 10-30 minutes à environ 15 secondes et de 4096x4096 de 1-3 heures à environ 1 minute. Boîte de dépôt source et source ci-dessous mises à jour.
  • Mise à jour 2: Correction d'un bug qui empêchait la génération d'images 4096x4096.
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;
  }

}

Voici un ensemble complet d'images 256x128 que j'aime bien:

Mode 1:

Mon favori du jeu d'origine (longueur maximale_de_passe = 512, longueur_de_passe = 2, aléatoire, affiché 2x, lien 256x128 ):

enter image description here

Autres (deux à gauche ordonnés, deux à droite, deux voies du haut en haut, deux en bas sans limite):

ordlimit randlimit ordnolimit randnolimit

Celui-ci peut être carrelé:

randtile

Mode 2:

diamonds flowers boxfade diagover bigdiamonds boîtes2 tessons

Ceux-ci peuvent être carrelés:

bigtile carreau de diamant papier cadeau

512x512 sélections:

Diamants juxtaposables, mon préféré du mode 2; vous pouvez voir dans celui-ci comment les chemins marchent autour d'objets existants:

entrez la description de l'image ici

Pas de chemin plus long et longueur de chemin max.

entrez la description de l'image ici

Mode aléatoire 1, mosaïque:

entrez la description de l'image ici

Plus de sélections:

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici

Tous les rendus 512x512 peuvent être trouvés dans le dossier dropbox (* _64.png).

2048x1024 et 4096x4096:

Celles-ci sont trop volumineuses pour être intégrées et tous les hôtes d'image que j'ai trouvés les abaissent à 1600x1200. Je suis actuellement en train de restituer un ensemble d'images 4096x4096, donc d'autres images seront bientôt disponibles. Au lieu d’inclure tous les liens ici, allez simplement les vérifier dans le dossier de la boîte de dépôt (* _128.png et * _256.png, remarque: les 4096x4096 sont trop gros pour l’aperçu de la boîte de dépôt, cliquez simplement sur "télécharger"). Voici quelques-uns de mes favoris, cependant:

2048x1024 gros diamants tiled (le même que j'ai lié au début de ce post)

2048x1024 diamants (j'adore celui-ci!), Réduits:

entrez la description de l'image ici

4096x4096 gros diamants tiled (Enfin, cliquez sur le lien "Télécharger" dans le lien Dropbox; il est trop volumineux pour leur outil de prévisualisation), avec une taille réduite:

4096x4096 gros diamants tiled

4096x4096 mode aléatoire 1 : entrez la description de l'image ici

4096x4096 un autre cool

Mise à jour: le jeu d'images prédéfinies 2048x1024 est terminé et dans la liste déroulante. L'ensemble 4096x4096 doit être effectué dans l'heure.

Il y en a des tas de bons, j'ai vraiment du mal à choisir ceux que je veux poster, alors s'il vous plaît, jetez un coup d'œil sur le lien du dossier!


6
Cela me rappelle des vues rapprochées de certains minéraux.
Morwenn

3
Cela ne faisait pas partie du concours, mais je pensais que c'était plutôt cool . J'ai appliqué un gros flou gaussien et une amélioration automatique du contraste à l'une des photos en mode aléatoire 1 de photoshop.
Jason C

2
whoa, ce sont des images cool!
Sevenseacat

2
Cela me rappelle les textures de Gustav Klimt.
Kim

2
Saviez-vous que vous pouvez créer un lien direct entre les images dans Dropbox? Il suffit de copier l'URL de téléchargement, supprimez la dl=1et la token_hash=<something>partie et faire un lien vers votre image comme ceci: [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). Autre astuce: vous pouvez compresser vos images (les résultats obtenus avec TruePNG ( téléchargement ) sont satisfaisants ). J'ai pu économiser 28,1% de la taille du fichier sur cette image .
user2428118

219

Python avec PIL

Ceci est basé sur une fractale newtonienne , spécialement pour z → z 5 - 1 . Comme il existe cinq racines, et donc cinq points de convergence, l’espace colorimétrique disponible est divisé en cinq régions, en fonction de Teinte. Les points individuels sont d'abord triés en fonction du nombre d'itérations nécessaires pour atteindre leur point de convergence, puis en fonction de la distance qui les sépare, les valeurs les plus anciennes se voyant attribuer une couleur plus lumineuse.

Mise à jour: gros rendus 4096x4096, hébergés sur allrgb.com .

Original (33,7 Mo)

Un gros plan du centre même (taille réelle):

Un point de vue différent utilisant ces valeurs:

xstart = 0
ystart = 0

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

Original (32,2 Mo)

Et un autre utilisant ceux-ci:

xstart = 0.5
ystart = 0.5

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

Original (27,2 Mo)


Animation

Sur demande, j'ai compilé une animation de zoom.

Point focal: ( 0.50051 , -0.50051 )
Facteur de zoom: 2 1/5

Le point focal est une valeur légèrement étrange, car je ne voulais pas zoomer sur un point noir. Le facteur de zoom est choisi de telle sorte qu'il double toutes les 5 images.

Un teaser 32x32:

Une version 256x256 peut être vue ici:
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

Il peut y avoir des points qui, mathématiquement, font un zoom "sur eux-mêmes", ce qui permettrait une animation infinie. Si je peux en identifier, je les ajouterai ici.


La source

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

dim  = (4096, 4096)
bits = 8

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

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

  V = cmax

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

  else:
    S = dmax/cmax

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

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

  return (H, S, V)

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

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

xstart = -2
ystart = -2

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

tol = 1e-12

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

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

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

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

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

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

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

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

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

img.save("out.png")

6
Enfin une fractale :) Aimez ceux-ci. De plus, le vert à 144 degrés est ma couleur préférée (par opposition au vert pur à 120 degrés qui est ennuyeux).
Mark Jeronimus

2
Je ne sais pas, j'aime un peu mieux les versions AllRGB; la nécessité d'utiliser tout l'espace de luminance souligne bien les gradients.
Ilmari Karonen

2
+1 Enfin de bonnes fractales! Le dernier est mon préféré. Vous devriez faire une vidéo en zoomant! (@Quincunx: J'ai vu le tien aussi; il avait mon vote depuis le premier jour!)
Jason C

1
@JasonC J'ai ajouté une animation;)
dimanche

2
@primo Je sais que je suis en retard, mais je voulais juste dire que ces images sont spectaculaires.
Ashwin Gupta

130

J'ai eu cette idée de l'algorithme de l'utilisateur fejesjoco et je voulais jouer un peu, alors j'ai commencé à écrire mon propre algorithme à partir de zéro.

Je publie ceci parce que j'estime que si je peux faire quelque chose de mieux * que le meilleur de vous, je ne pense pas que ce défi soit terminé. À titre de comparaison, il existe de superbes designs sur allRGB que je considère bien au-delà du niveau atteint ici et je n'ai aucune idée de la façon dont ils l'ont fait.

*) sera toujours décidé par votes

Cet algorithme:

  1. Commencez avec une (quelques) graine (s), avec des couleurs aussi proches que possible du noir.
  2. Conservez une liste de tous les pixels non visités et connectés à un point visité.
  3. Sélectionnez un ** point aléatoire dans cette liste
  4. Calculez la couleur moyenne de tous les pixels calculés [Modifier ... dans un carré de 9x9 à l'aide d'un noyau gaussien] connecté à 8 (c'est la raison pour laquelle il a l'air si lisse) Si vous n'en trouvez pas, prenez du noir.
  5. dans un cube 3x3x3 autour de cette couleur, recherchez une couleur inutilisée.
    • Lorsque plusieurs couleurs sont trouvées, prenez la plus sombre.
    • Lorsque plusieurs couleurs également sombres sont trouvées, prenez-en une au hasard parmi celles-ci.
    • Lorsque rien n’est trouvé, mettez à jour la plage de recherche sur 5x5x5, 7x7x7, etc. Répétez la procédure à partir de 5.
  6. Tracer pixel, mettre à jour la liste et répéter à partir de 3

J'ai également expérimenté différentes probabilités de choisir des points candidats en fonction du nombre de voisins visités du pixel sélectionné, mais cela ne faisait que ralentir l'algorithme sans le rendre plus joli. L'algorithme actuel n'utilise pas de probabilités et choisit un point aléatoire dans la liste. Cela provoque le remplissage rapide de points avec beaucoup de voisins, ce qui en fait simplement une balle solide en croissance avec un bord flou. Ceci évite également l'indisponibilité des couleurs voisines si les crevasses devaient être remplies plus tard dans le processus.

L'image est toroïdale.

Java

Télécharger: com.digitalmodularbibliothèque

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
  • origine 1 graine
  • 1 seconde

entrez la description de l'image ici

  • 2048 × 1024
  • légèrement en mosaïque vers le bureau 1920 × 1080
  • 30 secondes
  • négatif dans photoshop

entrez la description de l'image ici

  • 2048 × 1024
  • 8 graines
  • 27 secondes

entrez la description de l'image ici

  • 512 × 512
  • 40 graines aléatoires
  • 6 secondes

entrez la description de l'image ici

  • 4096 × 4096
  • 1 graine
  • Les stries deviennent nettement plus nettes (car on dirait qu'elles pourraient couper un poisson en sashimi)
  • On aurait dit que ça avait fini en 20 minutes, mais ... pas réussi pour une raison quelconque, alors maintenant, je lance 7 instances en parallèle en une nuit.

[Voir ci-dessous]

[Modifier]
** J'ai découvert que ma méthode de sélection des pixels n'était pas totalement aléatoire. Je pensais qu'avoir une permutation aléatoire de l'espace de recherche serait aléatoire et plus rapide que réel (car un point ne sera pas choisi deux fois par hasard. Cependant, en le remplaçant par de vrai aléatoire, j'obtiens systématiquement plus de points de bruit dans mon image.

[Code de la version 2 supprimé car je dépassais la limite de 30 000 caractères]

entrez la description de l'image ici

  • Augmentation du cube de recherche initial à 5x5x5

entrez la description de l'image ici

  • Encore plus grand, 9x9x9

entrez la description de l'image ici

  • Accident 1. Désactivation de la permutation afin que l'espace de recherche soit toujours linéaire.

entrez la description de l'image ici

  • Accident 2. Essayé une nouvelle technique de recherche en utilisant une file d'attente fifo. Reste à analyser cela, mais j’ai pensé que cela valait la peine d’être partagé.

entrez la description de l'image ici

  • Toujours choisir dans les X pixels inutilisés à partir du centre
  • X va de 0 à 8192 par pas de 256

L'image ne peut pas être téléchargée: "Oups! Quelque chose de mauvais est arrivé! Ce n'est pas vous, c'est nous. C'est notre faute." L'image est trop grande pour imgur. Essayer ailleurs ...

entrez la description de l'image ici

Lors de l’expérimentation d’un progiciel de planification, j’ai trouvé dans la digitalmodularbibliothèque un ordre de traitement des pixels (au lieu de la diffusion).

package demos;

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

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

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

    public static void main(String[] args) {

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

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

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

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

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

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

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

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

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

        new Thread(this).start();
    }

    @Override
    public void resized() {}

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

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

        // System.exit(0);
    }

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

        Arrays.fill(colorCube, false);

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

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

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

        timer.stop();
        draw();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return -1;
    }

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

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

entrez la description de l'image ici

  • Angulaire (64)

entrez la description de l'image ici

  • Tube cathodique

entrez la description de l'image ici

  • Tremblement

entrez la description de l'image ici

  • Fleur (5, X), où X varie de 0,5 à 20 par incréments de X = X × 1,2

entrez la description de l'image ici

  • Mod

entrez la description de l'image ici

  • Pythagoras

entrez la description de l'image ici

  • Radial

entrez la description de l'image ici

  • Aléatoire

entrez la description de l'image ici

  • Scanline

entrez la description de l'image ici

  • Spirale (X), où X varie de 0,1 à 200 par incréments de X = X × 1,2
  • Vous pouvez voir qu'il se situe entre Radial et Angular (5)

entrez la description de l'image ici

  • Divisé

entrez la description de l'image ici

  • SquareSpiral

entrez la description de l'image ici

  • XOR

entrez la description de l'image ici

Nouvel aliment oculaire

  • Effet de la sélection des couleurs par max(r, g, b)

entrez la description de l'image ici

  • Effet de la sélection des couleurs par min(r, g, b)
  • Notez que celui-ci a exactement les mêmes caractéristiques / détails que celui ci-dessus, mais avec des couleurs différentes! (même graine aléatoire)

entrez la description de l'image ici

  • Effet de la sélection des couleurs par max(r, min(g, b))

entrez la description de l'image ici

  • Effet de la sélection de couleur par valeur de gris 299*r + 436*g + 114*b

entrez la description de l'image ici

  • Effet de la sélection des couleurs par 1*r + 10*g + 100*b

entrez la description de l'image ici

  • Effet de la sélection des couleurs par 100*r + 10*g + 1*b

entrez la description de l'image ici

  • Heureux accidents en cas de 299*r + 436*g + 114*bdébordement dans un entier 32 bits

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici

  • Variante 3, avec valeur de gris et planificateur Radial

entrez la description de l'image ici

  • J'ai oublié comment j'ai créé ça

entrez la description de l'image ici

  • Le planificateur CRT avait également un bogue de dépassement d’entier (le ZIP) mis à jour, ce qui l’a fait démarrer à mi-chemin, avec 512 × 512 images, au lieu de se placer au centre. Voici à quoi cela est censé ressembler:

entrez la description de l'image ici entrez la description de l'image ici

  • InverseSpiralScheduler(64) (Nouveau)

entrez la description de l'image ici

  • Un autre XOR

entrez la description de l'image ici

  • Premier rendu 4096 réussi après le correctif. Je pense que c'était la version 3 SpiralScheduler(1)ou quelque chose

entrez la description de l'image ici (50 Mo !!)

  • Version 1 4096, mais j'ai accidentellement laissé les critères de couleur sur max()

entrez la description de l'image ici (50 Mo !!)

  • 4096, maintenant avec min()
  • Notez que celui-ci a exactement les mêmes caractéristiques / détails que celui ci-dessus, mais avec des couleurs différentes! (même graine aléatoire)
  • Heure: j'ai oublié de l'enregistrer mais l'horodatage du fichier est 3 minutes après l'image précédente

entrez la description de l'image ici (50 Mo !!)


Cool. Votre image finale est semblable à une deuxième idée que j'ai lancée, bien que j'ai le sentiment que la mienne ne sera pas aussi belle que ça. BTW, il en existe un similaire cool à allrgb.com/diffusive .
Jason C

C'était censé n'être qu'un teaser, mais je l'ai édité dans la crainte d'être signalé, ce qui est apparemment arrivé :)
Mark Jeronimus

2
Même les accidents ont l'air bien :). Le cube de couleur semble être une très bonne idée, et vos vitesses de rendu sont étonnantes, comparées aux miennes. Certains modèles sur allrgb ont une bonne description, par exemple allrgb.com/dla. J'aimerais avoir plus de temps pour faire plus d'expériences, il y a tellement de possibilités ...
fejesjoco

J'ai presque oublié, je viens de télécharger certains de mes gros rendus. Je pense que l’un d’eux, la couleur arc-en-ciel / encre renversée, est préférable à tout ce qui est allrgb :). Je suis d’accord, les autres ne sont pas aussi renversants, c’est pourquoi j’ai fait une vidéo pour en tirer quelque chose de plus :).
fejesjoco

Ajout du code source et du lien vers la bibliothèque Digisoft pour vous permettre de compiler mon code
Mark Jeronimus

72

C ++ avec Qt

Je te vois version:

entrez la description de l'image ici

en utilisant la distribution normale pour les couleurs:

entrez la description de l'image ici entrez la description de l'image ici

ou d'abord trié par rouge / teinte (avec un écart plus petit):

entrez la description de l'image ici entrez la description de l'image ici

ou d'autres distributions:

entrez la description de l'image ici entrez la description de l'image ici

Distribution de Cauchy (hsl / red):

entrez la description de l'image ici entrez la description de l'image ici

Cols triés par légèreté (hsl):

entrez la description de l'image ici

code source mis à jour - produit la 6ème image:

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

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

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

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

Bien fait. Cependant, ne peut pas image.pixel(x, y) == 0échouer et écraser le premier pixel placé?
Mark Jeronimus

@ Zom-B: c'est possible, mais le dernier sera noir et conforme aux règles.
Jaa-c

Aucun problème de règle cependant. Je pensais juste que vous l'aviez peut-être manqué. Peut aussi compter à partir de 1 alors. J'aime tes autres!
Mark Jeronimus

@ Zom-B: merci, je pourrais en ajouter un peu plus, je l'aime bien: P
Jaa-c

Celui avec deux cercles et celui en dessous ensemble ressemblent à un visage de singe.
Jason C

64

En Java:

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

import javax.imageio.ImageIO;

public class ImgColor {

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

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

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

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

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

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

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

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

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

}

et une image d'entrée:

maki

Je génère quelque chose comme ceci:

acidLemur

version non compressée ici: https://www.mediafire.com/?7g3fetvaqhoqgh8

Il faut environ 30 minutes à mon ordinateur pour créer une image 4096 ^ 2, ce qui représente une amélioration considérable par rapport aux 32 jours que ma première implémentation aurait pris.


1
Aie; 32 jours ne semblaient pas amusants ..... l'algorithme moyen dans fejesjocos répondre sur 4k avant d'optimiser aurait probablement pris plusieurs mois
masterX244

5
J'adore ses sourcils punk!
Level River St

45

Java avec BubbleSort

(Bubblesort n’est généralement pas très apprécié, mais pour ce défi, il a finalement eu une utilisation :) a généré une ligne avec tous les éléments distants de 4096 pas, puis l'a mélangée; le tri s'est bien déroulé et chacun a obtenu 1 ajouté à sa valeur lors de son tri afin que vous obteniez les valeurs triées et toutes les couleurs

Mise à jour du code source pour supprimer ces grosses bandes
(nécessite un peu de magie au niveau des bits: 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;
    }
}

Résultat:

Ancienne version

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

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

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

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

aperçu de sortie


Il existe déjà une version QuickSort sur la page allRGB.
Mark Jeronimus

1
@ Zom-B Quicksort est un algorithme différent de Bubblesort
masterX244

43

C

Crée un vortex, pour des raisons que je ne comprends pas, avec des images paires et impaires contenant des vortex complètement différents.

Voici un aperçu des 50 premières images impaires:

aperçu de vortex

Exemple d'image convertie de PPM en couverture complète de couleurs de démonstration:

échantillon d'image

Plus tard, quand tout est mélangé en gris, vous pouvez toujours le voir tourner: une séquence plus longue .

Code comme suit. Pour exécuter, inclure le numéro du cadre, par exemple:

./vortex 35 > 35.ppm

J'ai utilisé cela pour obtenir un GIF animé:

convertir-différer 10 `ls * .ppm | trier -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
Vous savez que c'est C quand il se passe quelque chose pour "des raisons que je ne comprends pas".
Nit

2
Oui, d'habitude, je sais à quoi m'attendre, mais ici, je m'amusais à voir quels motifs je pouvais obtenir, et cette séquence d'ordre sans fin dans le chaos est apparue.

8
Cela tourbillonne parce que votre fonction de comparaison ne suit pas l'inégalité du triangle. Par exemple, r> b, b> g, g> r. Je ne peux même pas le porter en Java parce que mergesort repose sur cette propriété même. Je reçois donc l'exception "La méthode de comparaison enfreint son contrat général!"
Mark Jeronimus

2
Je vais essayer p->b * 6 - q->g;mais si ça détruit le vortex, ça ne va pas le réparer!

4
+1 pour des raisons que je ne comprends pas.
Jason C

40

Java

Variations d'un sélecteur de couleurs en 512x512. Ce n'est pas un code élégant , mais j'aime bien les jolies images:

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

Comme écrit, il génère:

pas de shuffle

Si vous le lancez shuffle_block = true, il mélange les couleurs de chaque bloc 64x64:

bloquer au hasard

Sinon, si vous le lancez shuffle_radius > 0, il mélange chaque pixel avec un pixel aléatoire shuffle_radiusdans x / y. Après avoir joué avec différentes tailles, j'aime bien un rayon de 32 pixels, car cela brouille les lignes sans trop déplacer les éléments:

entrez la description de l'image ici


3
ooh ces photos sont les plus jolies
sevenseacat

Ce sont vraiment super
Matthew

37

En traitement

Je commence tout juste à utiliser C (j'ai programmé dans d'autres langues), mais j'ai trouvé les graphiques dans Visual C difficiles à suivre. J'ai donc téléchargé ce programme de traitement utilisé par @ace.

Voici mon code et mon algorithme.

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

Algorithme

Commencez avec des carrés 4x4 de toutes les combinaisons possibles de 32 valeurs de vert et de bleu, en x, y. formatage, faisant un carré de 128x128 Chaque carré de 4x4 a 16 pixels, créez donc une image miroir à côté de lui pour donner 32 pixels de chaque combinaison possible de vert et de bleu, image par image

(Bizarrement, le vert intégral semble plus brillant que le cyan complet. Ce doit être une illusion d'optique. clarifiée dans les commentaires)

Dans le carré de gauche, ajoutez les valeurs rouges de 0 à 15. Pour le carré de droite, XOR ces valeurs avec 16, pour faire les valeurs 16-31.

entrez la description de l'image ici

Sortie 256x128

Cela donne la sortie dans l'image du haut ci-dessous.

Cependant, chaque pixel ne diffère de son image miroir que par le bit le plus significatif de la valeur rouge. Donc, je peux appliquer une condition avec la variablec , pour inverser le XOR, ce qui a le même effet que d'échanger ces deux pixels.

Un exemple de ceci est donné dans l'image du bas ci-dessous (si nous décommentons la ligne de code qui est actuellement commentée.)

entrez la description de l'image ici

512 x 512 - Hommage au Marylin d'Andy Warhol

Inspiré par la réponse de Quincunx à cette question avec un "sourire diabolique" dans les cercles rouges à main levée, voici ma version de la célèbre photo. L'original avait en réalité 25 Marylins de couleur et 25 Marylins en noir et blanc. Il s’agissait de l’hommage rendu par Warhol à Marylin après sa mort prématurée. Voir http://en.wikipedia.org/wiki/Marilyn_Diptych

J'ai changé de fonction après avoir découvert que Processing rend ceux que j'avais utilisés en 256x128 comme semi-transparents. Les nouveaux sont opaques.

Et bien que l'image ne soit pas complètement algorithmique, je l'aime plutôt.

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

}

entrez la description de l'image ici

512x512 Crépuscule sur un lac avec des montagnes au loin

Ici, une image entièrement algorithmique. J'ai joué avec changer la couleur que je module avec la condition, mais je reviens à la conclusion que le rouge fonctionne mieux. Comme pour l'image de Marylin, je dessine d'abord les montagnes, puis je sélectionne la luminosité de cette image pour écraser l'image RVB positive tout en effectuant une copie dans la moitié négative. Une légère différence est que le bas de beaucoup de montagnes (car elles sont toutes dessinées de la même taille) s’étend au-dessous de la zone de lecture; cette zone est donc simplement rognée pendant le processus de lecture (ce qui donne donc l’impression souhaitée de montagnes de tailles différentes. )

Dans celui-ci, j'utilise une cellule 8x4 de 32 rouges pour le positif et les 32 rouges restants pour le négatif.

Notez la commande expicit frameRate (1) à la fin de mon code. J'ai découvert que sans cette commande, Processing utiliserait 100% d'un coeur de mon processeur, même si le dessin était terminé. Autant que je sache, il n’ya pas de fonction de veille, tout ce que vous pouvez faire est de réduire la fréquence des scrutations.

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

entrez la description de l'image ici


Parce que ce n'est pas du tout cyan. C'est (0,217,217). Les 32 combinaisons sont toutes présentes mais non étirées [0,255]. Edit: Vous utilisez les étapes de 7 mais je ne trouve pas cela dans le code. Doit être une chose de traitement.
Mark Jeronimus

@steveverrill Dans Processing, vous pouvez save("filename.png")enregistrer le tampon d'image actuel dans une image. D'autres formats d'image sont également supportés. Cela vous évitera d'avoir à prendre des captures d'écran. L'image est enregistrée dans le dossier de l'esquisse.
Jason C

@Jasonc merci pour le tuyau, j'étais sûr qu'il devait y avoir un moyen, mais je ne pense pas que je vais l'éditer. J'ai laissé le cadre entourant partiellement les images pour les séparer (2 fichiers pour de si petites images, c'était exagéré.) Je veux faire des images en 512x512 (et il y en a une en particulier pour laquelle j'ai une idée), je vais donc les télécharger de la manière suivante. vous suggérez.
Level River St

1
@steveverrill Haha, les Warhol sont une belle touche.
Jason C

@ Zom-B Processing semble faire beaucoup de choses qui (de manière gênante) ne sont pas mentionnées dans sa documentation: ne pas utiliser les 256 valeurs de canal de couleur logiques complètes dans sa sortie physique, fusionner les couleurs lorsque vous ne le souhaitez pas, utiliser un noyau complet de mon processeur même après avoir fini de dessiner. C'est quand même simple d'intégrer et de pouvoir résoudre ces problèmes une fois que vous savez qu'ils sont là (sauf le premier, je ne l'ai pas encore résolu ...)
Level River St

35

Je viens d'organiser toutes les couleurs 16 bits (5r, 6g, 5b) sur une courbe de Hilbert en JavaScript.

couleurs de courbe hilbert

Image précédente (pas la courbe de Hilbert):

courbe d'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);

Modifier : Il se trouve qu'il y avait un bug dans ma fonction pour calculer la courbe de Hilbert et que c'était incorrect; à savoir, r.x = (p.x & s) > 0; r.y = (p.y & s) > 0;changé pourr.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

Edit 2: Une autre fractale:

Sierpinsky

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


Agréable! Bienvenue chez PPCG.
Jonathan Van Matre

À quoi ressemble-t-il lorsque la promenade dans le cube de couleur est aussi une courbe 3D de Hilbert? Modifier nm. c'est ce que quelqu'un a fait.
Mark Jeronimus

35

C #: Optimisation de similarité locale itérative

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

Idée

Commençons par un mélange aléatoire:

entrez la description de l'image ici

Ensuite, nous sélectionnons deux pixels au hasard et les échangeons. Si cela n'augmente pas la similarité des pixels avec leurs voisins, nous échangeons en arrière et essayons à nouveau. Nous répétons ce processus encore et encore.

Après quelques générations (5000), les différences ne sont plus aussi évidentes ...

entrez la description de l'image ici

Mais plus il court (25000), ...

entrez la description de l'image ici

... plus certains modèles commencent à apparaître (100000).

entrez la description de l'image ici

En utilisant différentes définitions de voisinage , nous pouvons influencer ces modèles et leur stabilité ou non. La Kernelest une matrice similaire à celle utilisée pour les filtres en traitement d'image . Il spécifie les poids de chaque voisin utilisé pour le calcul du delta RVB.

Résultats

Voici quelques-uns des résultats que j'ai créés. Les vidéos montrent le processus itératif (1 image == 1000 générations), mais malheureusement, la qualité n’est pas optimale (vimeo, YouTube, etc. ne prend pas correctement en charge de si petites dimensions). Je pourrai plus tard essayer de créer des vidéos de meilleure qualité.

0 1 0
1 X 1
0 1 0

185000 générations:

entrez la description de l'image ici Vidéo (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 générations:

entrez la description de l'image ici Vidéo (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 générations:

entrez la description de l'image ici Vidéo (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

Ce noyau est intéressant car, du fait de son asymétrie, les motifs ne sont pas stables et l’image entière se déplace vers la droite au fil des générations.

2331000 générations:

entrez la description de l'image ici Vidéo (01:10)


Grands résultats (512x512)

L'utilisation des noyaux ci-dessus avec une dimension d'image plus grande crée les mêmes motifs locaux, couvrant une surface totale plus grande. Une image 512x512 prend entre 1 et 2 millions de générations à se stabiliser.

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici


OK, passons maintenant au sérieux et créons des patterns plus locaux, moins volumineux, avec un noyau 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

Cela augmente considérablement le temps de calcul par génération. 1,71 million de générations et 20 heures plus tard:

entrez la description de l'image ici


1
Cela prend un certain temps pour y arriver, mais le résultat final est assez sympa.
Primo

Coïncidence intéressante, j'ai un article sur le même sujet: nayuki.io/page/simulated-annealing-demo
Nayuki

30

Java

Avec quelques variantes de mon autre réponse, nous pouvons obtenir des résultats très intéressants.

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

Le code important est ici:

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

});

Sortie (capture d'écran):

entrez la description de l'image ici

Changez le comparateur en ceci:

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

Et nous obtenons ceci:

entrez la description de l'image ici

Une autre variante:

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

entrez la description de l'image ici

Encore une autre variante (me rappelle les automates cellulaires):

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

entrez la description de l'image ici

Encore une autre variante (nouveau favori personnel):

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

entrez la description de l'image ici

Cela a l'air tellement fractal-ly. XOR est si beau, surtout en gros plan:

entrez la description de l'image ici

Un autre gros plan:

entrez la description de l'image ici

Et maintenant, le triangle de Sierpinski, incliné:

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

entrez la description de l'image ici


8
La première image ressemble à une photo de puce de processeur ou de mémoire
Nick T

@ NickT Voici un dé de mémoire (selon Google Images) pour le contraste: files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg
Justin

4
La mémoire est tellement sans forme ... probablement un processeur très multicœur alors: extremetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg
Nick T

1
J'aime beaucoup ces derniers. Très glitchy mais avec une structure d'organisation sous-jacente. Je veux un tapis tissé comme ça XOR design!
Jonathan Van Matre

Ce sont vraiment cool; ils me rappellent un peu un jeu d'arcade cassé ou un nes.
Jason C

29

Java

Je ne savais pas vraiment comment créer des couleurs de 15 ou 18 bits, alors je viens de laisser le bit le moins significatif de l'octet de chaque canal pour créer 2 ^ 18 couleurs différentes de 24 bits. La plupart du bruit est éliminé par le tri, mais une élimination efficace du bruit semble nécessiter la comparaison de plus de deux éléments à la fois, comme le fait Comparator. J'essaierai de manipuler des noyaux plus gros, mais entre-temps, c'est à peu près tout ce que j'ai pu faire de mieux.

entrez la description de l'image ici

Cliquez pour l'image HD n ° 2

Image basse résolution n ° 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
Ce second mérite vraiment d'avoir une version 24 bits 4096 x 4096 ...
trichoplax

Imgur traite l'image depuis environ une demi-heure. Je suppose que c'est probablement essayer de le compresser. Quoi qu'il en soit, j'ai ajouté un lien: SSend.it/hj4ovh
John P

2
Il y a un problème avec le téléchargement.
SuperJedi224

28

Scala

Je commande toutes les couleurs en parcourant une courbe Hilbert en 3 dimensions via un système L . Je parcourt ensuite les pixels de l'image de sortie le long d'une courbe de Hilbert à 2 dimensions et y dispose toutes les couleurs.

512 x 512 sorties:

entrez la description de l'image ici

Voici le code. La plupart de ces activités ne traitent que de la logique et du calcul mathématique de la navigation dans les trois dimensions via pitch / roll / yaw. Je suis sûr qu'il y avait une meilleure façon de faire cette partie, mais bon.

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, des choses vraiment cool dans ce défi. J'ai tenté ma chance en C # et généré une image 4096x4096 en environ 3 minutes (processeur i7) en utilisant toutes les couleurs via la logique Random Walk.

Ok, donc pour le code. Après avoir été frustré par des heures de recherche et avoir essayé de générer chaque couleur HSL à l'aide de boucles for en code, j'ai décidé de créer un fichier plat à partir duquel lire les couleurs HSL. Ce que j'ai fait a été de créer chaque liste de couleurs RVB dans une liste, puis j'ai commandé par teinte, luminosité, puis saturation. Ensuite, j'ai enregistré la liste dans un fichier texte. ColorData est juste une petite classe que j'ai écrite qui accepte une couleur RVB et stocke également l'équivalent HSL. Ce code est un énorme mangeur de RAM. Utilisé environ 4 Go de 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;
    }


}

Avec ça à l'écart. J'ai écrit une classe pour obtenir la couleur suivante du fichier généré. Il vous permet de définir le début et la fin de la teinte. En réalité, cela pourrait et devrait probablement être généralisé à la dimension par laquelle le fichier a été trié en premier. De plus, je me rends compte que pour améliorer les performances ici, j'aurais pu simplement mettre les valeurs RVB dans le fichier et conserver chaque ligne à une longueur fixe. De cette façon, j'aurais facilement pu spécifier le décalage d'octet au lieu de parcourir toutes les lignes jusqu'à atteindre la ligne où je voulais commencer. Mais ce n’était pas vraiment un coup de performance pour moi. Mais voici cette classe

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

Alors maintenant que nous avons le fichier couleur et que nous avons un moyen de lire le fichier, nous pouvons maintenant créer l'image. J'ai utilisé une classe que j'ai trouvée pour améliorer les performances de définition des pixels dans une image bitmap, appelée LockBitmap. Source LockBitmap

J'ai créé une petite classe Vector2 pour stocker les emplacements de coordonnées

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

Et j'ai également créé une classe appelée SearchArea, utile pour la recherche de pixels voisins. Vous spécifiez le pixel pour lequel vous souhaitez rechercher des voisins, les limites dans lesquelles effectuer la recherche et la taille du "carré voisin" dans lequel effectuer la recherche. Ainsi, si la taille est 3, cela signifie que vous recherchez un carré 3x3, avec le pixel spécifié au centre.

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

}

Voici la classe qui choisit le prochain voisin. En gros, il y a 2 modes de recherche. A) Le carré complet, B) juste le périmètre du carré. C’est une optimisation que j’ai faite pour ne plus avoir à chercher dans le carré complet après avoir réalisé que ce dernier était plein. La carte DepthMap constituait une optimisation supplémentaire pour empêcher la recherche répétée des mêmes carrés. Cependant, je n’ai pas totalement optimisé cela. Chaque appel à GetNeothers fera toujours la recherche complète en carré en premier. Je sais que je pourrais optimiser cela pour ne faire la recherche de périmètre qu'après avoir complété la totalité du carré initial. Je ne connaissais pas encore l'optimisation, et même sans cela, le code est assez rapide. Les lignes "verrouillées" commentées sont parce que j’utilisais Parallel.ForEach à un moment donné, mais j’ai réalisé que je devais écrire plus de code que je ne le souhaitais pour ce lol.

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

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

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

        if (depth > depthMax)
        {
            return pixels;
        }

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

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

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

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

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

Ok super, alors maintenant voici la classe qui crée l'image

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

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

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

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

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

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

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

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

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

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

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

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

        }

    }

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


}

Et voici un exemple d'implémentation:

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

    }
}

Si RandomWalkSegments = 1, il commence simplement à marcher où que vous le disiez et commence à la première couleur du fichier.

J'admets que ce n'est pas le code le plus propre, mais il tourne très vite!

Production recadrée

3 chemins

128 chemins

MODIFIER:

J'ai donc découvert OpenGL et Shaders. J'ai généré un 4096x4096 en utilisant toutes les couleurs flamboyantes sur le GPU avec 2 scripts de shader simples. La sortie est ennuyeuse, mais je me suis dit que quelqu'un pourrait trouver cela intéressant et proposer quelques idées intéressantes:

Vertex Shader

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

Frag Shader

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

Edit (15/10/16): Je voulais juste montrer une preuve de concept d'un algorithme génétique. J'exécute TOUJOURS ce code 24 heures plus tard avec un jeu de couleurs aléatoires 100x100, mais jusqu'ici le rendu est magnifique!entrez la description de l'image ici

Edit (26/10/16): J'utilise le code d'algorithme génétique depuis 12 jours maintenant ... et il optimise toujours la sortie. Il a fondamentalement convergé vers un minimum local, mais il semble qu’il y ait encore de meilleures améliorations:entrez la description de l'image ici

Edit: 12/08/17 - J'ai écrit un nouvel algorithme de marche aléatoire - vous spécifiez en gros un certain nombre de "marcheurs", mais au lieu de marcher au hasard - ils choisiront au hasard un autre marcheur et l'éviteront (choisissez le prochain pixel disponible le plus éloigné ) - ou marchez vers eux (choisissez le prochain pixel disponible le plus proche d’eux). Voici un exemple de sortie en niveaux de gris (je ferai un rendu complet des couleurs en 4096x4096 après avoir câblé la coloration!):entrez la description de l'image ici


4
Un peu tardif, mais bienvenue à PPCG! C'est un excellent premier post.
Un spaghetto

1
Je vous remercie! J'ai hâte de relever d'autres défis! Je fais plus de choses de codage d'images ces derniers temps, c'est mon nouveau passe
applejacks01

Waouh ils sont géniaux; Je suis content d’être revenu sur ce message aujourd’hui et d’avoir vérifié tous les éléments postérieurs.
Jason C

Je vous remercie! Je suis en train de faire un algorithme génétique codant maintenant pour produire des gradients intéressants. Fondamentalement, prenez 10000 couleurs, formant une grille 100x100. Pour chaque pixel, obtenez les pixels voisins. Pour chacun, obtenez la distance CIEDE2000. Résume ça. Faites la somme pour tous les 10000 pixels. L'algorithme génétique tente de réduire cette somme totale. C'est lent, mais pour une image 20x20, sa sortie est vraiment intéressante
applejacks01

J'aime tout particulièrement le résultat de cette solution.
r_alex_hall le

22

Toile HTML5 + JavaScript

Je l'appelle randoGraph et vous pouvez en créer autant que vous voulez ici

Quelques exemples:

Exemple 1

exemple 2

exemple 3

exemple 4

exemple 5

exemple 6

exemple 7

Par exemple, dans Firefox, vous pouvez cliquer avec le bouton droit de la souris sur la toile (une fois celle-ci terminée) et l'enregistrer en tant qu'image. Produire une image 4096x4096 est un type de problème dû à la limite de mémoire de certains navigateurs.

L'idée est assez simple mais chaque image est unique. Nous créons d'abord la palette de couleurs. Puis, en commençant par X points, nous sélectionnons des couleurs aléatoires dans la palette et nous les positionnons (chaque fois que nous sélectionnons une couleur, nous la supprimons de la palette) et nous enregistrons où nous la mettons de manière à ne pas placer le pixel suivant à la même position.

Pour chaque pixel qui est tangent à cela, nous créons un nombre (X) de couleurs possibles, puis nous sélectionnons les plus pertinentes pour ce pixel. Cela continue jusqu'à ce que l'image soit complète.

Le code 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>

Et le JavaScript pour 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;
};

C'est bien mais cela ressemble à la réponse en C # de fejesjoco . Est-ce seulement par hasard?
AL

1
Les algorithmes sont là et tout le monde peut lire et comprendre ce qui est vraiment différent. Cette réponse publiée après la réponse C # de fejesjoco a été déclarée gagnante, motivée par la beauté de son résultat. Ensuite, j'ai pensé à une approche totalement différente du traitement et de la sélection des couleurs voisines, et c'est tout. Bien sûr, les deux réponses ont la même base, comme la distribution uniforme des couleurs utilisées dans le spectre visible, le concept de couleurs pertinentes et les points de départ, peut-être la recherche de cette base, une personne pourrait penser que les images produites ressemblent parfois à des ressemblances.
KonstantinosX

D'accord, je suis désolé si vous pensiez que je critiquais votre réponse. Je me demandais simplement si la réponse de fejesjoco vous inspirait, puisque le résultat obtenu était similaire.
AL

1
«Définir les méthodes d'une classe à l'intérieur du constructeur au lieu d'utiliser la chaîne de prototypes est vraiment inefficace, surtout si cette classe est utilisée plusieurs fois.» Est un commentaire très intéressant, commente Patrick Roberts. Avez-vous une référence avec un exemple qui valide cela? , J'aimerais sincèrement savoir si cette revendication est fondée (afin de cesser de l'utiliser) et en quoi consiste-t-elle.
KonstantinosX

2
En ce qui concerne l’utilisation du prototype: cela fonctionne plus ou moins comme une méthode statique. Lorsque vous définissez la fonction dans le littéral d’objet, chaque nouvel objet que vous créez doit également créer une nouvelle copie de la fonction et les stocker avec cette instance d’objet (16 millions d’objets couleur signifient donc 16 millions de copies de cette fonction Mémoire). Par comparaison, utiliser un prototype ne le créera qu’une fois, et sera associé à la "classe" plutôt qu’à l’objet. Cela présente des avantages évidents en termes de mémoire et de vitesse.
Mwr247

20

Python

Voici donc ma solution en python, il faut presque une heure pour en créer une, il y a donc probablement une optimisation à faire:

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

Voici quelques exemples de résultats:

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici


1
On dirait des formes d'onde sonores délirantes
Mark Jeronimus

19

Java

J'ai décidé d'essayer ce défi. J'ai été inspiré par cette réponse à un autre code golf. Mon programme génère des images plus laides, mais elles ont toutes les couleurs.

Aussi, ma première fois au golf. :)

(Les images 4k étaient trop grandes pour ma petite vitesse de téléchargement. J'ai essayé d'en télécharger une, mais au bout d'une heure, elle n'a pas été téléchargée. Vous pouvez générer la vôtre.)

Fermer:

Génère une image en 70 secondes sur ma machine, prend environ 1,5 Go de mémoire pour générer

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]

Résultat (2x):

256x128 2x

Image originale en 256x128

Modifier:

en remplaçant Log10 [i] par Log10 [i] / 5, vous obtenez: entrez la description de l'image ici

Le code ci-dessus est lié au recuit simulé. Vu sous cet angle, la deuxième image est créée avec une "température" plus élevée dans les 10 ^ 6 premières étapes. Plus la "température" est élevée, plus il y a de permutations entre les pixels, alors que dans la première image, la structure de l'image ordonnée est encore légèrement visible.


17

JavaScript

Je suis toujours étudiant et j'écris pour la première fois, donc mes codes sont probablement en désordre et je ne suis pas sûr à 100% que mes photos ont toutes les couleurs nécessaires, mais j'étais super contente de mes résultats et j'ai donc pensé les poster.

Je sais que le concours est terminé, mais j’ai vraiment aimé les résultats obtenus et j’ai toujours aimé l’aspect des labyrinthes générés par retour arrière récursif; j’ai donc pensé qu’il serait peut-être cool de voir à quoi ressemblerait un pixel de couleur. Je commence donc par générer toutes les couleurs d'un tableau, puis je fais le retour arrière récursif tout en supprimant les couleurs du tableau.

Voici mon JSFiddle http://jsfiddle.net/Kuligoawesome/3VsCu/

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

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

var imageData;
var grid;
var colors;

var currentPos;
var prevPositions;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return availableSpaces;
}

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

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

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

Image 256x128, couleurs triées rouge-> vert-> bleu
Couleurs triées RVB

Image 256x128, couleurs triées bleu-> vert-> rouge
BGR Couleurs Triées

Image 256x128, couleurs triées teinte-> luminance-> saturation
HLS couleurs triées

Et enfin un GIF généré
Labyrinthe Couleur GIF


Vos couleurs sont coupées dans les régions les plus lumineuses, ce qui entraîne des doublons. Changer r * Math.ceil(255 / (colorSteps - 1)pour r * Math.floor(255 / (colorSteps - 1), ou même mieux: r * 255 / (colorSteps - 1)(non testé, puisque vous n'avez pas fourni de texte)
Mark Jeronimus

Oups, oui j'ai eu le sentiment que cela allait causer des problèmes, espérons que c'est réglé maintenant, et désolé pour le manque de jsfiddle (je ne savais pas que ça existait!) Merci!
Kuligoawesome

J'aime la sortie ordonnée de ce chaos / bruit et une autre solution qui produit une sortie similaire.
r_alex_hall le

17

C #

J'ai donc commencé à travailler là-dessus simplement comme un exercice amusant et je me suis retrouvé avec une sortie qui au moins me semble très chouette. La principale différence entre ma solution (au moins) la plupart des autres solutions réside dans le fait que je génère exactement le nombre de couleurs nécessaires pour commencer et en espaçant uniformément la génération du blanc pur au noir pur. Je mets également les couleurs en marche en spirale et choisit la couleur suivante en fonction de la moyenne des différences de couleur entre tous les voisins définis.

Voici un petit exemple de sortie que j’ai produit jusqu’à présent. Je travaille sur un rendu 4K, mais j’attends qu’il prenne plus d’une journée à terminer.

Voici un exemple de la sortie spec à 256x128:

Spec Render

Quelques images plus grandes avec des temps de rendu toujours raisonnables:

Rendu à 360 x 240

La deuxième exécution à 360 x 240 a produit une image beaucoup plus douce

Rendu # 2 à 360 x 240

Après l'amélioration des performances, j'ai pu exécuter un rendu HD qui a pris 2 jours. Je n'ai pas encore abandonné le 4K, mais cela pourrait prendre des semaines.

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

Si vous avez des idées sur la façon d'améliorer les performances de l'algorithme de sélection des couleurs, merci de me le faire savoir. En l'état, le rendu 360 * 240 prend environ 15 minutes. Je ne crois pas que cela puisse être paramétré, mais je me demande s’il existe un moyen plus rapide d’obtenir le diff de couleur le plus bas.
Phaeze

Comment une image de 360 ​​* 240 constitue-t-elle «toutes les couleurs»? Comment produisez-vous cbrt (360 * 240) = 44.208377983684639269357874002958 couleurs par composant?
Mark Jeronimus

Quelle langue est-ce? Randomiser une liste et Random est une mauvaise idée malgré tout, car selon l'algorithme et l'implémentation, il peut en résulter un résultat biaisé ou une exception précisant que "Comparison method violates its general contract!": car le contrat l'indique (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. Pour randomiser une liste, utilisez la méthode Shuffle fournie. ( colors.Shuffle()?)
Mark Jeronimus

@MarkJeronimus J'admets que j'ai raté la spécification concernant l'image 256x128, je vais faire les rendus simples en utilisant ces tailles, je me suis concentré sur le fait que chaque pixel est un aspect couleur unique du défi et des rendus plus grands que d'autres soumissions.
Phaeze

@ MarkJeronimus Je me rends compte que le tri aléatoire est mauvais, en fait, il y a un commentaire qui en dit autant. Ce n’était qu’un vestige d’une autre approche que j’avais commencé à adopter et que je privilégiais l’obtention des rendus de grande taille car ils prenaient beaucoup de temps.
Phaeze

16

Aller

En voici un autre de moi, je pense que cela donne des résultats plus intéressants:

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

Cela commence par le même motif que le gif dans mon autre réponse . Ensuite, cela se mélange:

juste du bruit

Plus je fais appel à un algorithme de comparaison de voisins peu inspiré, plus le motif de l'arc-en-ciel devient apparent.

Voici 16384:

un arc-en-ciel très bruyant à 16384 itérations

Et 65536:

entrez la description de l'image ici


6
+1 J'aime le fait qu'un motif en émerge; vous devriez en faire une animation!
Jason C

16

Ces images sont "Rainbow de Langton". Ils sont dessinés plutôt simplement: lorsque la fourmi de Langton se déplace, une couleur est dessinée sur chaque pixel la première fois que ce pixel est visualisé. La couleur à dessiner ensuite est simplement incrémentée de 1, ce qui garantit l’utilisation de 2 ^ 15 couleurs, une pour chaque pixel.

EDIT: J'ai créé une version qui rend les images 4096X4096 en utilisant 2 ^ 24 couleurs. Les couleurs sont également «réfléchies», ce qui permet d'obtenir des dégradés lisses et doux. Je ne fournirai que des liens vers eux car ils sont énormes (> 28 Mo)

Langton's Rainbow large, règle LR

Langton's Rainbow large, règle LLRR

// Fin de l'édition.

Ceci pour le jeu de règles classique LR:

Langton's Rainbow LR

Voici LLRR:

LLRR Rainbow de Langton

Enfin, celui-ci utilise le jeu de règles LRRRRRLRL:

Langton's Rainbow LRRRRRLLR

Écrit en C ++, en utilisant CImg pour les graphiques. Je devrais également mentionner comment les couleurs ont été sélectionnées: Tout d’abord, j’utilise un raccourci non signé pour contenir les données de couleur RVB. Chaque fois qu'une cellule est vue pour la première fois, je décale les bits d'un multiple de 5, ET de 31, puis multiplie de 8. Ensuite, la couleur courte non signée est incrémentée de 1. Cela produit des valeurs allant de 0 à 248 au maximum. Cependant, j'ai soustrait cette valeur de 255 dans les composantes rouge et bleue, donc R et B sont exprimés en multiples de 8, à partir de 255, jusqu'à 7:

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

Toutefois, cela ne s'applique pas à la composante verte, qui est un multiple de 8 de 0 à 248. Dans tous les cas, chaque pixel doit contenir une couleur unique.

Quoi qu'il en soit, le code source est ci-dessous:

#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
Bienvenue et rejoignez le club. Peut-être que c'est intéressant d'essayer d'autres sources de code.google.com/p/ruletablerepository/wiki/… (j'ai participé à cela)
Mark Jeronimus

3
Les liens d’image sont morts parce que Dropbox a tué les dossiers publics.
user2428118

15

Rubis

J'ai décidé de créer le PNG à partir de rien, car je pensais que ce serait intéressant. Ce code est littéralement les fournir en sortie premières données binaires dans un fichier.

J'ai fait la version 512x512. (L'algorithme est plutôt inintéressant, cependant.) Il se termine en 3 secondes environ sur ma machine.

require 'zlib'

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

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

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

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

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

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

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

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

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

Sortie (en all_colors.png) (cliquez sur l’une des images pour l’agrandir):

Sortie

Sortie en dégradé légèrement plus intéressante (en remplaçant la 4ème à la dernière ligne par }.shuffle }):

Sortie 2

Et en le changeant en }.shuffle }.shuffle, vous obtenez des lignes de couleurs folles:

Sortie 3


C'est vraiment cool. Y a-t-il un moyen de le rendre plus joli cependant? Peut-être randomiser les pixels? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian Ok, édité
Bouton de porte

Beaucoup mieux !!!!!!!

1
Que se passera-t-il si vous changiez la 4ème à la dernière ligne }.shuffle }.shuffle }.shuffle?
John Odom

6
@ John Erm, erreur de syntaxe, probablement?
Poignée de porte

14

Python

plasma

Utilisation de python pour trier les couleurs par luminance, générer un motif de luminance et sélectionner la couleur la plus appropriée. Les pixels sont itérés dans un ordre aléatoire, de sorte que les correspondances de luminance moins favorables qui se produisent naturellement lorsque la liste des couleurs disponibles devient plus petite soient également réparties dans l'image.

#!/usr/bin/env python

from PIL import Image
from math import pi, sin, cos
import random

WIDTH = 256
HEIGHT = 128

img = Image.new("RGB", (WIDTH, HEIGHT))

colors = [(x >> 10, (x >> 5) & 31, x & 31) for x in range(32768)]
colors = [(x[0] << 3, x[1] << 3, x[2] << 3) for x in colors]
colors.sort(key=lambda x: x[0] * 0.2126 + x[1] * 0.7152 + x[2] * 0.0722)

def get_pixel(lum):
    for i in range(len(colors)):
        c = colors[i]
        if c[0] * 0.2126 + c[1] * 0.7152 + c[2] * 0.0722 > lum:
            break
    return colors.pop(i)

def plasma(x, y):
    x -= WIDTH / 2
    p = sin(pi * x / (32 + 10 * sin(y * pi / 32)))
    p *= cos(pi * y / 64)
    return 128 + 127 * p

xy = []
for x in range(WIDTH):
    for y in range(HEIGHT):
        xy.append((x, y))
random.shuffle(xy)

count = 0
for x, y in xy:
    l = int(plasma(x, y))
    img.putpixel((x, y), get_pixel(plasma(x, y)))
    count += 1
    if not count & 255:
        print "%d pixels rendered" % count

img.save("test.png")

13

Java

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

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

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096 ; x++) {
                points.add(new Point(x, y));
            }
        }
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

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

Je suis allé pour 4096 par 4096 parce que je ne pouvais pas comprendre comment obtenir toutes les couleurs sans le faire.

Sortie:

Trop gros pour tenir ici. Ceci est une capture d'écran:

entrez la description de l'image ici

Avec un peu de changement, nous pouvons obtenir une plus belle image:

Ajoutez Collections.shuffle(points, new Random(0));entre générer les points et faire les couleurs:

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

entrez la description de l'image ici

Fermer:

entrez la description de l'image ici


29
Vous appelez une grosse tâche grise "belle"?
Poignée de porte

22
@Doorknob Oui. Je l'appelle très belle. Je trouve incroyable que toutes les couleurs puissent être arrangées dans une grosse tâche grise. Je trouve le blob plus intéressant lorsque je fais un zoom avant. Avec un peu plus de détails, vous pouvez voir à quel point le rng non aléatoire de Java est. Lorsque nous effectuons un zoom avant encore plus, comme sur la deuxième capture d'écran, le nombre de couleurs contenues dans cette chose devient clair. Lorsque je fais un zoom encore plus loin, cela ressemble à un programme Piet.
Justin

J'ai obtenu les couleurs dans des versions plus petites en laissant tomber les bits inférieurs.
Mark Jeronimus

Oui, les bits inférieurs pour r, get bséparément, mais je traitais avec eux comme un numéro.
Justin

Je vois que vous avez compris le meilleur de la magie dans votre prochaine réponse. Sur le sujet, il peut être intéressant d’expérimenter avec votre propre Randomsous-classe qui produit encore moins de nombres aléatoires idéaux.
Mark Jeronimus

13

C ++ 11

( Mise à jour: ce n'est qu'après que j'ai remarqué qu'une approche similaire avait déjà été essayée - avec plus de patience quant au nombre d'itérations.)

Pour chaque pixel, je définis un ensemble de pixels voisins. Je définis la différence entre deux pixels comme étant la somme des carrés de leurs différences R / V / B. La pénalité d'un pixel donné est alors la somme des écarts entre le pixel et ses voisins.

Maintenant, je commence par générer une permutation aléatoire, puis je commence à choisir des paires de pixels aléatoires. Si permuter les deux pixels réduit la somme des pénalités totales de tous les pixels, l'échange est exécuté. Je le répète un million de fois.

La sortie est au format PPM, que j'ai converti en PNG à l'aide d'utilitaires standard.

La source:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <random>

static std::mt19937 rng;

class Pixel
{
public:
    int r, g, b;

    Pixel() : r(0), g(0), b(0) {}
    Pixel(int r, int g, int b) : r(r), g(g), b(b) {}

    void swap(Pixel& p)
    {
        int r = this->r,  g = this->g,    b = this->b;
        this->r = p.r;    this->g = p.g;  this->b = p.b;
        p.r = r;          p.g = g;        p.b = b;
    }
};

class Image
{
public:
    static const int width = 256;
    static const int height = 128;
    static const int step = 32;
    Pixel pixel[width*height];
    int penalty[width*height];
    std::vector<int>** neighbors;

    Image()
    {
        if (step*step*step != width*height)
        {
            std::cerr << "parameter mismatch" << std::endl;
            exit(EXIT_FAILURE);
        }

        neighbors = new std::vector<int>*[width*height];

        for (int i = 0; i < width*height; i++)
        {
            penalty[i] = -1;
            neighbors[i] = pixelNeighbors(i);
        }

        int i = 0;
        for (int r = 0; r < step; r++)
        for (int g = 0; g < step; g++)
        for (int b = 0; b < step; b++)
        {
            pixel[i].r = r * 255 / (step-1);
            pixel[i].g = g * 255 / (step-1);
            pixel[i].b = b * 255 / (step-1);
            i++;
        }
    }

    ~Image()
    {
        for (int i = 0; i < width*height; i++)
        {
            delete neighbors[i];
        }
        delete [] neighbors;
    }

    std::vector<int>* pixelNeighbors(const int pi)
    {
        // 01: X-shaped structure
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return abs(i) == abs(j); };
        //
        // 02: boring blobs
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return true; };
        //
        // 03: cross-shaped
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return i==0 || j == 0; };
        //
        // 04: stripes
        const int iRad = 1, jRad = 5;
        auto condition = [](int i, int j) { return i==0 || j == 0; };

        std::vector<int>* v = new std::vector<int>;

        int x = pi % width;
        int y = pi / width;

        for (int i = -iRad; i <= iRad; i++)
        for (int j = -jRad; j <= jRad; j++)
        {
            if (!condition(i,j))
                continue;

            int xx = x + i;
            int yy = y + j;

            if (xx < 0 || xx >= width || yy < 0 || yy >= height)
                continue;

            v->push_back(xx + yy*width);
        }

        return v;
    }

    void shuffle()
    {
        for (int i = 0; i < width*height; i++)
        {
            std::uniform_int_distribution<int> dist(i, width*height - 1);
            int j = dist(rng);
            pixel[i].swap(pixel[j]);
        }
    }

    void writePPM(const char* filename)
    {
        std::ofstream fd;
        fd.open(filename);
        if (!fd.is_open())
        {
            std::cerr << "failed to open file " << filename
                      << "for writing" << std::endl;
            exit(EXIT_FAILURE);
        }
        fd << "P3\n" << width << " " << height << "\n255\n";
        for (int i = 0; i < width*height; i++)
        {
            fd << pixel[i].r << " " << pixel[i].g << " " << pixel[i].b << "\n";
        }
        fd.close();
    }

    void updatePixelNeighborhoodPenalty(const int pi)
    {
        for (auto j : *neighbors[pi])
            updatePixelPenalty(j);
    }

    void updatePixelPenalty(const int pi)
    {
        auto pow2 = [](int x) { return x*x; };
        int pen = 0;
        Pixel* p1 = &pixel[pi];
        for (auto j : *neighbors[pi])
        {
            Pixel* p2 = &pixel[j];
            pen += pow2(p1->r - p2->r) + pow2(p1->g - p2->g) + pow2(p1->b - p2->b);
        }
        penalty[pi] = pen / neighbors[pi]->size();
    }

    int getPixelPenalty(const int pi)
    {
        if (penalty[pi] == (-1))
        {
            updatePixelPenalty(pi);
        }
        return penalty[pi];
    }

    int getPixelNeighborhoodPenalty(const int pi)
    {
        int sum = 0;
        for (auto j : *neighbors[pi])
        {
            sum += getPixelPenalty(j);
        }
        return sum;
    }

    void iterate()
    {
        std::uniform_int_distribution<int> dist(0, width*height - 1);       

        int i = dist(rng);
        int j = dist(rng);

        int sumBefore = getPixelNeighborhoodPenalty(i)
                        + getPixelNeighborhoodPenalty(j);

        int oldPenalty[width*height];
        std::copy(std::begin(penalty), std::end(penalty), std::begin(oldPenalty));

        pixel[i].swap(pixel[j]);
        updatePixelNeighborhoodPenalty(i);
        updatePixelNeighborhoodPenalty(j);

        int sumAfter = getPixelNeighborhoodPenalty(i)
                       + getPixelNeighborhoodPenalty(j);

        if (sumAfter > sumBefore)
        {
            // undo the change
            pixel[i].swap(pixel[j]);
            std::copy(std::begin(oldPenalty), std::end(oldPenalty), std::begin(penalty));
        }
    }
};

int main(int argc, char* argv[])
{
    int seed;
    if (argc >= 2)
    {
        seed = atoi(argv[1]);
    }
    else
    {
        std::random_device rd;
        seed = rd();
    }
    std::cout << "seed = " << seed << std::endl;
    rng.seed(seed);

    const int numIters = 1000000;
    const int progressUpdIvl = numIters / 100;
    Image img;
    img.shuffle();
    for (int i = 0; i < numIters; i++)
    {
        img.iterate();
        if (i % progressUpdIvl == 0)
        {
            std::cout << "\r" << 100 * i / numIters << "%";
            std::flush(std::cout);
        }
    }
    std::cout << "\rfinished!" << std::endl;
    img.writePPM("AllColors2.ppm");

    return EXIT_SUCCESS;
}

Faire varier le pas des voisins donne des résultats différents. Cela peut être peaufiné dans la fonction Image :: pixelNeighbors (). Le code comprend des exemples pour quatre options: (voir la source)

exemple 01 exemple 02 exemple 03 exemple 04

Edit: un autre exemple similaire au quatrième ci-dessus mais avec un noyau plus gros et plus d'itérations:

exemple 05

Un de plus: utiliser

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

et dix millions d'itérations, j'ai eu ceci:

exemple 06


11

Ce n'est pas le code le plus élégant, mais intéressant sur deux points: calculer le nombre de couleurs à partir des dimensions (tant que le produit des dimensions est une puissance de deux) et utiliser l'espace colorimétrique trippant:

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

Certaines variations intéressantes peuvent être obtenues simplement en modifiant la clause OrderBy:

X:

entrez la description de l'image ici

y:

entrez la description de l'image ici

z:

entrez la description de l'image ici

Y:

entrez la description de l'image ici

Je voudrais pouvoir comprendre ce qui causait les lignes impaires dans les trois premiers


2
Ces lignes étranges sont probablement le biais d'une sorte ou d'une méthode de recherche (recherche binaire / tri rapide?)
Mark Jeronimus

En fait, j'aime vraiment les lignes ici.
Jason C

11

Java

C'était une bien meilleure idée. Ceci est un code Java très court; la méthode principale ne compte que 13 lignes:

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

Génère des blocs de "sélecteurs de couleurs". Fondamentalement, dans le premier bloc, r=0dans le second, r=1etc. Dans chaque bloc, gincréments par rapport à xet bpar rapport à y.

J'aime beaucoup les opérateurs de bitwise. Permettez-moi de décomposer la setRGBdéclaration:

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

En raison des opérateurs au niveau du bit, cela ne prend que 7 secondes. Si le r & 15est remplacé par r % 16, cela prend 9 secondes.

J'ai choisi le 4096 x 4096

Sortie (capture d'écran, trop grande sinon):

entrez la description de l'image ici

Sortie avec un sourire diabolique dessiné dessus par des cercles à main levée:

entrez la description de l'image ici


2
Lien vers l'original pour que je puisse vérifier la validité (compter les couleurs)
Mark Jeronimus

2
Lol! J'ai oublié que je peux exécuter du code Java. La première image passe, et je ne peux pas reproduire la deuxième image (lol) ☺
Mark Jeronimus

16
Les cercles à main levée ont tous la même couleur, disqualifiés. : P
Nick T

3
@Quincunx +1 si vous pouvez dessiner un visage effrayant tout en respectant les exigences de couleur!
Jason C

2
@JasonC Voir ma réponse. Le crédit va à Quincunx pour l'inspiration.
Level River St
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.