Imaginez un chemin bidimensionnel continu qui ne peut tourner qu'à gauche, à droite ou aller tout droit, ne peut pas se croiser et doit remplir une grille rectangulaire telle que la grille de pixels dans une image. Nous appellerons ce genre de chemin un serpent .
Cet exemple agrandi montre un chemin de serpent dans une grille 10 × 4 qui commence en rouge et augmente de teinte d'environ 2% à chaque étape jusqu'à ce qu'il soit violet. (Les lignes noires ne font que souligner la direction que cela prend.)
Objectif
Le but de ce concours de popularité est d'écrire un algorithme qui tente de recréer une image donnée en utilisant un seul serpent dont la couleur change continuellement par petites quantités.
Votre programme doit prendre une image en couleurs vraies de n'importe quelle taille ainsi qu'une valeur en virgule flottante entre 0 et 1 inclus, la tolérance .
La tolérance définit la quantité maximale que la couleur du serpent peut changer à chaque étape de la taille d'un pixel. Nous définirons la distance entre deux couleurs RVB comme la distance euclidienne entre les deux points RVB lorsqu'elle est disposée sur un cube de couleurs RVB . La distance sera alors normalisée de sorte que la distance maximale est 1 et la distance minimale est 0.
Pseudocode de distance de couleur: (suppose que toutes les valeurs d'entrée sont des entiers dans la plage [0, 255]
; la sortie est normalisée.)
function ColorDistance(r1, g1, b1, r2, g2, b2)
d = sqrt((r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2)
return d / (255 * sqrt(3))
Si le résultat de l'appel de cette fonction sur la couleur actuelle du serpent et sur une autre couleur est supérieur à la tolérance donnée, le serpent peut ne pas changer dans cette autre couleur.
Si vous préférez, vous pouvez utiliser une fonction de distance de couleur différente. Il doit s'agir de quelque chose de précis et de bien documenté, comme ceux répertoriés sur http://en.wikipedia.org/wiki/Color_difference . Vous devez également le normaliser pour qu'il soit à l'intérieur [0, 1]
, c'est-à-dire que la distance maximale possible doit être 1 et le minimum doit être 0. Dites-nous dans votre réponse si vous utilisez une métrique de distance différente.
Images de test
Vous devez bien sûr publier vos images de sortie (et même des animations du serpent qui grandit si vous le souhaitez). Je suggère de publier une variété de ces images en utilisant différentes tolérances faibles (peut-être autour de 0,005 à 0,03).
Critères de victoire
Comme indiqué, il s'agit d'un concours de popularité. La réponse votée la plus élevée gagnera. Les réponses qui fournissent la représentation la plus précise et la plus esthétique du "chemin du serpent" des images d'entrée doivent être votées.
Tout utilisateur qui soumet par malveillance des images qui ne sont pas de véritables serpents sera définitivement disqualifié.
Remarques
- Un seul chemin de serpent peut être utilisé et il doit remplir complètement l'image sans toucher deux fois le même pixel.
- Le serpent peut commencer et se terminer n'importe où dans l'image.
- Le serpent peut commencer comme n'importe quelle couleur.
- Le serpent doit rester dans les limites de l'image. Les bornes ne sont pas cycliques.
- Le serpent ne peut pas se déplacer en diagonale ou sur plus d'un pixel à la fois.
la source
Réponses:
Python
Je génère un chemin dynamique pour minimiser les changements de couleur lors du voyage du serpent. Voici quelques images:
tolérance = 0,01
Chemins de couleurs cycliques pour les images ci-dessus (du bleu au rouge, devenant plus vertes à mesure qu'elles se répètent):
Le chemin est généré en commençant par un chemin initial, puis en y ajoutant des boucles 2x2 jusqu'à ce que l'image soit remplie. L'avantage de cette méthode est que les boucles peuvent être ajoutées n'importe où sur le chemin, vous ne pouvez donc pas vous peindre dans un coin et avoir plus de liberté pour construire le chemin que vous voulez. Je garde une trace des boucles possibles adjacentes au chemin actuel et les stocke dans un tas, pondéré par le changement de couleur le long de la boucle. Je saute ensuite la boucle avec le moins de changement de couleur et l'ajoute au chemin, et je répète jusqu'à ce que l'image soit remplie.
En fait, je fais le suivi des boucles seules («DetourBlock» dans le code), puis je reconstruis le chemin; c'était une erreur car il y a des cas spéciaux pour la largeur / hauteur impaire et j'ai passé plusieurs heures à déboguer la méthode de reconstruction. Tant pis.
La métrique de génération de chemin doit être ajustée et j'ai également une idée pour une meilleure colorisation, mais je pensais que je sortirais cela en premier car cela fonctionne assez bien. À l'exception de celui-ci, qui semble meilleur dans certains des chemins fixes:
Voici le code Python, avec des excuses pour mes habitudes de codage atroces:
Et quelques images supplémentaires avec une très faible tolérance de 0,001 :
Et aussi le grand chemin des vagues car il est soigné:
MODIFIER
La génération de chemin semble meilleure lorsque l'on minimise la distance de couleur entre les couleurs moyennes des blocs adjacents, plutôt que de minimiser la somme des distances de couleur entre leurs pixels adjacents. En outre, il s'avère que vous pouvez faire la moyenne des couleurs de deux chemins de serpent conformes à la tolérance et vous retrouver avec un autre chemin de serpent conforme à la tolérance. Je traverse donc le chemin dans les deux sens et les moyenne, ce qui aplanit beaucoup d'artefacts. Zombie Lena et Scary Hands Mona sont beaucoup mieux. Versions finales:
Tolérance 0,01 :
Tolérance 0,001 :
la source
Java
Mon programme génère un chemin de serpent pour une largeur et une hauteur données, en utilisant un algorithme similaire à celui qui génère la courbe de Hilbert.
(petit jeu: dans l'image ci-dessus, le serpent commence dans le coin supérieur gauche. Pouvez-vous trouver où il finit? Bonne chance :)
Voici les résultats pour différentes valeurs de tolérance:
Tolérance = 0,01
Tolérance = 0,05
Tolérance = 0,1
Tolérance = 0,01
Avec des blocs de pixels 4x4 et le chemin visible
Calcul du chemin du serpent
Un chemin de serpent est stocké dans un tableau d'entiers à double dimension. Le serpent pénètre toujours dans la grille par le coin supérieur gauche. Il y a 4 opérations de base que mon programme peut faire sur un chemin de serpent donné:
créer un nouveau chemin de serpent pour une grille de largeur 1 ou hauteur 1. Le chemin est juste une ligne simple qui va de gauche à droite ou de haut en bas, selon le cas.
augmenter la hauteur de la grille, en ajoutant en haut un chemin de serpent de gauche à droite, puis en reflétant la grille (le serpent doit toujours entrer dans la grille par le coin supérieur gauche)
augmenter la largeur de la grille, en ajoutant à gauche un chemin de serpent de haut en bas, puis en retournant la grille (le serpent doit toujours entrer dans la grille par le coin supérieur gauche)
doubler la dimension de la grille à l'aide d'un algorithme de "style Hilbert" (voir description ci-dessous)
En utilisant une série de ces opérations atomiques, le programme est capable de générer un chemin de serpent de n'importe quelle taille donnée.
Le code ci-dessous calcule (dans l'ordre inverse) quelles opérations seront nécessaires pour obtenir une largeur et une hauteur données. Une fois calculées, les actions sont exécutées une par une jusqu'à ce que nous obtenions un chemin de serpent de la taille attendue.
Doubler la taille du chemin du serpent:
L'algorithme qui double la taille fonctionne comme suit:
Considérez ce nœud qui est lié à DROITE et BAS. Je veux doubler sa taille.
Il y a 2 façons de doubler sa taille et de garder les mêmes sorties (droite et bas):
ou
Pour déterminer lequel choisir, j'ai besoin de gérer pour chaque direction de noeud une valeur de "décalage", indiquant si la porte de sortie est décalée vers la gauche / droite ou vers le haut / vers le bas. Je suis le chemin comme le ferait le serpent et je mets à jour la valeur de décalage le long du chemin. La valeur de décalage détermine de manière unique le bloc étendu que je dois utiliser pour l'étape suivante.
la source
Python
Voici un algorithme très simple pour commencer. Il commence en haut à gauche de l'image et tourne en spirale dans le sens des aiguilles d'une montre, rendant la couleur aussi proche que possible de la couleur du pixel suivant tout en restant dans la tolérance.
Il faut une minute ou deux pour exécuter les images plus grandes, bien que je sois sûr que la logique de spirale pourrait être largement optimisée.
Résultats
Ils sont intéressants mais pas magnifiques. Étonnamment, une tolérance supérieure à 0,1 produit des résultats d'aspect assez précis.
La Grande Vague avec une tolérance de 0,03:
Joconde à 0,02 tolérance:
Lena à 0,03 tolérance, puis 0,01, puis 0,005, puis 0,003:
Divers à 0,1 tolérance, puis 0,07, puis 0,04, puis 0,01:
la source
Cobra
Remplit l'image d'un serpent comme:
Cela permet un réglage des couleurs beaucoup plus rapide que les lignes dans des directions alternées, mais ne devient pas aussi polyédrique qu'une version à 3 larges.
Même à des tolérances très faibles, les bords d'une image sont toujours visibles (bien qu'à la perte de détails dans des résolutions plus petites).
0,01
0,1
0,01
0,01
0,1
0,03
0,005
la source
C #
Snake commence au pixel supérieur gauche avec la couleur blanche et alterne de gauche à droite puis de droite à gauche dans l'image.
Tolérance de l'image résultante = 0,1
la source