Comment créer de l'art procédural en moins de 100 lignes de code



L'art génératif (art génératif ou procédural) peut vous faire peur si vous ne l'avez jamais rencontré auparavant. En bref, c'est un concept d'art qui se crée littéralement et ne nécessite pas de connaissances en programmation hardcore pour la première fois. J'ai donc décidé de diluer un peu notre bande, ils ont roulé.

Qu'est-ce que l'art génératif?


C'est le résultat d'un système qui prend ses propres décisions concernant un objet au lieu d'une personne. Un système peut être aussi simple qu'un programme Python unique s'il a des règles et un moment de chance.

Avec la programmation, il est assez facile de trouver des règles et des restrictions. Il existe des déclarations conditionnelles pour cela. Mais trouver des moyens de faire en sorte que ces règles créent quelque chose d'intéressant n'est peut-être pas aussi simple.


Le jeu de la vie de Conway

Le jeu de la vie de Conway est un ensemble bien connu de quatre règles simples qui définissent la "naissance" et la "mort" de chaque cellule du système. Chaque règle joue un rôle dans la promotion du système à chaque génération. Bien que les règles soient simples et faciles à comprendre, des modèles complexes émergent rapidement et produisent finalement des résultats intéressants.

Les règles peuvent être responsables de jeter les bases de quelque chose d'intéressant, mais même quelque chose d'aussi excitant que le jeu de la vie de Conway est prévisible. Les quatre règles sont les facteurs déterminants pour chaque génération. Par conséquent, pour obtenir des résultats inattendus, vous devez introduire la randomisation dans l'état initial des cellules. En commençant par une matrice aléatoire, chaque exécution sera unique sans avoir besoin de changer les règles.

Les meilleurs exemples d'art génératif sont ceux qui trouvent une combinaison de prévisibilité et de hasard pour créer quelque chose d'intéressant qui est statistiquement impossible à répéter.

Pourquoi devriez-vous essayer cela?


L'art génératif ne sera pas toujours ce sur quoi vous voulez passer du temps. Mais si vous décidez d'y travailler, vous pouvez compter sur les avantages suivants:

  • Expérience. L'art génératif est une autre occasion de perfectionner des compétences nouvelles et anciennes. Il peut servir d'entrée pour l'élaboration de concepts tels que les algorithmes, les structures de données et même de nouveaux langages.
  • Des résultats tangibles. En programmation, nous voyons rarement les résultats physiques de nos efforts. Eh bien, ou du moins je ne le vois pas. En ce moment dans mon salon, il y a plusieurs affiches avec des impressions de mon art procédural. Et j'aime que cela se fasse par code.
  • Des designs attrayants. Tout le monde a eu l'expérience d'expliquer un projet personnel à quelqu'un, peut-être même lors d'un entretien. L'art génératif parle de lui-même. La plupart des gens apprécieront les résultats, même s'ils ne peuvent pas pleinement comprendre les méthodes.

Par où commencer?


Commencer avec l'art génératif est le même processus que commencer avec n'importe quel autre projet. L'étape la plus importante est de proposer une idée ou de la trouver pour un développement ultérieur. Une fois que vous avez un objectif, vous pouvez commencer à travailler sur sa réalisation.

La plupart de mes projets créatifs sont en Python. C'est un langage assez simple avec de nombreux packages utiles qui aident au traitement d'image. Par exemple, Oreiller .

Heureusement, vous n'avez pas à chercher longtemps par où commencer - ci-dessous, je partagerai mon code.

Générateur de sprites


Ce projet a commencé lorsque j'ai vu un article avec un générateur de sprites écrit en JavaScript. Le programme a créé des sprites d'art 5 × 5 pixels avec des options de couleurs aléatoires, et son résultat ressemblait à des envahisseurs spatiaux multicolores.

Je voulais pratiquer le traitement d'image en Python, j'ai donc décidé de recréer ce concept moi-même. De plus, j'ai pensé que je pouvais l'étendre, car le projet d'origine était très limité dans la taille des sprites. Et je veux indiquer non seulement la taille des sprites, mais aussi leur nombre et même la taille de l'image.

Voici deux résultats différents de mon programme:


7x7-30–1900


43x43-6–1900

Ces deux images sont complètement différentes l'une de l'autre, mais elles sont toutes deux le résultat du même système. Sans parler du fait qu'en raison de la complexité et de la génération aléatoire de sprites, il y a une forte probabilité que, même avec les mêmes arguments, ces images soient toujours uniques. Je l'adore.

L'environnement


Avant de vous familiariser avec le générateur de sprites, vous devez préparer une petite base pour le travail.

Si vous n'avez jamais travaillé avec Python auparavant, téléchargez Python 2.7.10 . Au début, j'ai eu des problèmes avec la configuration de l'environnement, si vous les rencontrez également, regardez dans des environnements virtuels . Et assurez-vous que Pillow est également installé.

Après avoir configuré l'environnement, vous pouvez copier mon code dans un fichier avec l'extension .py et exécuter la commande suivante:

python spritething.py [SPRITE_DIMENSIONS] [NUMBER] [IMAGE_SIZE] 

Par exemple, la commande pour créer la première matrice de sprites serait:

 python spritething.py 7 30 1900 

Code


 import PIL, random, sys from PIL import Image, ImageDraw origDimension = 1500 r = lambda: random.randint(50,215) rc = lambda: (r(), r(), r()) listSym = [] def create_square(border, draw, randColor, element, size): if (element == int(size/2)): draw.rectangle(border, randColor) elif (len(listSym) == element+1): draw.rectangle(border,listSym.pop()) else: listSym.append(randColor) draw.rectangle(border, randColor) def create_invader(border, draw, size): x0, y0, x1, y1 = border squareSize = (x1-x0)/size randColors = [rc(), rc(), rc(), (0,0,0), (0,0,0), (0,0,0)] i = 1 for y in range(0, size): I *= -1 element = 0 for x in range(0, size): topLeftX = x*squareSize + x0 topLeftY = y*squareSize + y0 botRightX = topLeftX + squareSize botRightY = topLeftY + squareSize create_square((topLeftX, topLeftY, botRightX, botRightY), draw, random.choice(randColors), element, size) if (element == int(size/2) or element == 0): I *= -1; element += I def main(size, invaders, imgSize): origDimension = imgSize origImage = Image.new('RGB', (origDimension, origDimension)) draw = ImageDraw.Draw(origImage) invaderSize = origDimension/invaders padding = invaderSize/size for x in range(0, invaders): for y in range(0, invaders): topLeftX = x*invaderSize + padding/2 topLeftY = y*invaderSize + padding/2 botRightX = topLeftX + invaderSize - padding botRightY = topLeftY + invaderSize - padding create_invader((topLeftX, topLeftY, botRightX, botRightY), draw, size) origImage.save(«Examples/Example-«+str(size)+»x»+str(size)+»-«+str(invaders)+»-«+str(imgSize)+».jpg») if __name__ == «__main__»: main(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])) 

Cette solution est encore loin d'être parfaite, mais elle montre que la création d'art génératif ne nécessite pas une tonne de code. Je vais vous expliquer les points clés.

La fonction principale commence par la création de l'image d'origine et la détermination de la taille des sprites. Deux boucles for sont chargées de déterminer la bordure de chaque image-objet, en divisant essentiellement la taille de l'image par le nombre d'images-objets demandées. Ces valeurs sont utilisées pour déterminer les coordonnées de chacune d'entre elles.

Jetez un œil à l'image ci-dessous. Imaginez que chacun des quatre carrés est un sprite de taille 1. La bordure qui est passée à la fonction suivante fait référence aux coordonnées des coins supérieur gauche et inférieur droit. Ainsi, le tuple dans le sprite supérieur gauche sera (0,0,1,1), et le tuple dans le coin supérieur droit sera (1,0,2,1). Ils seront utilisés comme dimensions et coordonnées de base pour les carrés de chaque sprite.


Exemple de définition de bordures de sprite

La fonction create_invader définit la bordure de chaque carré à l'intérieur du sprite. Le même processus de détermination de la frontière est appliqué ici et est présenté ci-dessous, seulement au lieu de l'image complète, nous utilisons une bordure prédéfinie pour travailler à l'intérieur. Ces coordonnées finales pour chaque carré seront utilisées dans la fonction suivante pour dessiner un sprite.


Exemple de panne de sprite 3 × 3

Pour déterminer la couleur, un simple tableau de trois tuples RVB aléatoires et de trois noirs est utilisé pour simuler 50% de chances d'être dessiné. Les fonctions lambda en haut du code sont responsables de la génération des valeurs RVB.

Le véritable truc de cette fonction est de créer une symétrie. Chaque carré est associé à une valeur d'élément. La figure ci-dessous montre comment les valeurs des éléments augmentent lorsqu'ils atteignent le centre, puis diminuent. Les carrés avec des valeurs d'élément correspondantes sont affichés dans la même couleur.


Valeurs des éléments et couleurs symétriques pour une chaîne dans un sprite 7 × 7

Comme create_square obtient ses paramètres de create_invader , il utilise la file d'attente et les valeurs des éléments précédents pour assurer la symétrie. Lorsque les valeurs apparaissent pour la première fois, leurs couleurs sont placées dans la file d'attente et les carrés de miroir suppriment les couleurs.


Processus de création complet

Je comprends à quel point il est difficile de lire la solution de quelqu'un d'autre au problème et le code tordu, mais j'espère que vous trouverez cette application. Ce sera cool si vous abandonnez complètement mon code et trouvez une solution complètement différente.

Conclusion


L'art génératif met du temps à atteindre son plein potentiel. En général, il peut y avoir plus de projets utiles que l'art génératif, ce qui ne vaut pas toujours le temps. Mais c'est très amusant et on ne sait jamais où cela peut être utile.

Source: https://habr.com/ru/post/fr429078/


All Articles