Come creare arte generativa in meno di 100 righe di codice

L'arte generativa, come qualsiasi argomento di programmazione, può intimidire se non l'hai mai provata prima. Mi sono sempre interessato perché amo trovare nuovi modi in cui la programmazione può essere utilizzata in modo creativo. Inoltre, penso che chiunque possa apprezzare il concetto di opera d'arte che letteralmente crea se stessa.

Cos'è l'arte generativa?

L'arte generativa è l'output di un sistema che prende le proprie decisioni sull'opera, piuttosto che su un essere umano. Il sistema potrebbe essere semplice come un singolo programma Python, purché abbia regole e qualche aspetto di casualità.

Con la programmazione, è abbastanza semplice trovare regole e vincoli. Sono tutte le dichiarazioni condizionali. Detto questo, trovare modi per fare in modo che queste regole creino qualcosa di interessante può essere complicato.

The Game of Life è un famoso insieme di quattro semplici regole che determinano la "nascita" e la "morte" di ogni cellula del sistema. Ciascuna delle regole gioca un ruolo nel far avanzare il sistema attraverso ogni generazione. Sebbene le regole siano semplici e facili da capire, i modelli complessi iniziano rapidamente ad emergere e alla fine formano risultati affascinanti.

Le regole possono essere responsabili della creazione delle fondamenta di qualcosa di interessante, ma anche qualcosa di così eccitante come Game of Life di Conway è prevedibile. Poiché le quattro regole sono i fattori determinanti per ogni generazione, il modo per produrre risultati imprevedibili è introdurre la randomizzazione allo stato iniziale delle cellule. Iniziare con una matrice casuale renderà unica ogni esecuzione senza la necessità di modificare le regole.

I migliori esempi di arte generativa sono quelli che trovano una combinazione di prevedibilità e casualità per creare qualcosa di interessante che è anche statisticamente irriproducibile .

Perché dovresti provarlo?

Non tutti i progetti collaterali sono uguali e l'arte generativa potrebbe non essere qualcosa su cui sei incline a passare del tempo. Se decidi di lavorare a un progetto, tuttavia, puoi aspettarti questi vantaggi:

  • Esperienza - L'arte generativa è solo un'altra opportunità per affinare alcune nuove e vecchie abilità. Può fungere da gateway per praticare concetti come algoritmi, strutture dati e persino nuovi linguaggi.
  • Risultati tangibili - Nel mondo della programmazione raramente vediamo qualcosa di fisico uscire dai nostri sforzi, o almeno non lo faccio. In questo momento ho alcuni poster nel mio soggiorno che mostrano stampe della mia arte generativa e mi piace che la programmazione ne sia responsabile.
  • Progetti attraenti - Abbiamo tutti avuto l'esperienza di spiegare un progetto personale a qualcuno, forse anche durante un colloquio, senza un modo semplice per trasmettere lo sforzo ei risultati del progetto. L'arte generativa parla da sola e quasi tutti saranno colpiti dalle tue creazioni, anche se non riescono a comprendere appieno i metodi.

Da dove dovresti iniziare?

Iniziare con l'arte generativa è lo stesso processo di qualsiasi progetto, il passo più cruciale è trovare un'idea o trovarne una su cui costruire. Una volta che hai un obiettivo in mente, puoi iniziare a lavorare sulla tecnologia necessaria per raggiungerlo.

La maggior parte dei miei progetti di arte generativa sono stati realizzati in Python. È un linguaggio abbastanza facile a cui abituarsi e ha alcuni pacchetti incredibili disponibili per aiutare con la manipolazione delle immagini, come Pillow.

Fortunatamente per te, non è necessario cercare molto lontano per un punto di partenza, perché di seguito ho fornito un codice con cui giocare.

Generatore di sprite

Questo progetto è iniziato quando ho visto un post che mostrava un generatore di sprite scritto in Javascript. Il programma ha creato sprite di pixel art 5x5 con alcune opzioni di colore casuali e il suo output somigliava a invasori spaziali multicolori.

Sapevo di voler praticare la manipolazione delle immagini in Python, quindi ho pensato che avrei potuto provare a ricreare questo concetto da solo. Inoltre, ho pensato di poterlo espandere poiché il progetto originale era così limitato nelle dimensioni degli sprite. Volevo essere in grado di specificare non solo la dimensione, ma anche il numero di essi e persino la dimensione dell'immagine.

Ecco uno sguardo a due diversi output dalla soluzione che ho ottenuto:

Queste due immagini non si assomigliano affatto, ma sono entrambe il risultato dello stesso sistema. Per non parlare, a causa della complessità dell'immagine e della casualità della generazione dello sprite, c'è un'altissima probabilità che anche con gli stessi argomenti, queste immagini saranno per sempre uniche nel loro genere. Lo adoro.

L'ambiente

Se vuoi iniziare a giocare con il generatore di sprite, c'è un piccolo lavoro di base che deve essere fatto prima.

Configurare un ambiente adeguato con Python può essere complicato. Se non hai mai lavorato con Python prima, probabilmente dovrai scaricare Python 2.7.10. Inizialmente ho avuto problemi a configurare l'ambiente, quindi se inizi a riscontrare problemi, puoi fare quello che ho fatto io e guardare negli ambienti virtuali. Ultimo ma non meno importante, assicurati di avere installato anche Pillow.

Una volta configurato l'ambiente, puoi copiare il mio codice in un file con estensione .py ed eseguirlo con il seguente comando:

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

Ad esempio, il comando per creare la prima matrice di sprite dall'alto sarebbe:

python spritething.py 7 30 1900

Il codice

import PIL, random, sysfrom 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]))

Questa soluzione è molto lontana dall'essere perfetta, ma mostra che la creazione di arte generativa non richiede un sacco di codice. Farò del mio meglio per spiegare i pezzi chiave.

La funzione principale inizia creando l'immagine iniziale e determinando la dimensione degli sprite. I due cicli for sono responsabili della definizione di un bordo per ogni sprite, dividendo sostanzialmente le dimensioni dell'immagine per il numero di sprite richiesti. Questi valori vengono utilizzati per determinare le coordinate per ciascuno.

Ignoriamo il riempimento e diamo un'occhiata all'immagine qui sotto. Immagina che ciascuno dei quattro quadrati rappresenti uno sprite con una dimensione di 1. Il bordo che viene passato alla funzione successiva si riferisce alle coordinate in alto a sinistra e in basso a destra. Quindi la tupla per lo sprite in alto a sinistra sarebbe (0,0,1,1) mentre la tupla per lo sprite in alto a destra sarebbe (1,0,2,1). Questi saranno usati come dimensioni e coordinate di base per i quadrati di ogni sprite.

La funzione create_invader determina il bordo per ogni quadrato all'interno dello sprite. Lo stesso processo per determinare il bordo viene applicato qui e rappresentato di seguito, solo che al posto dell'immagine completa utilizziamo un bordo predeterminato per lavorare all'interno. Queste coordinate finali per ogni quadrato verranno utilizzate nella funzione successiva per disegnare effettivamente lo sprite.

To determine the color, a simple array of three random RGB tuples and three blacks are used to simulate a 50% chance of being drawn. The lambda functions near the top of the code are responsible for generating the RGB values.

The real trick of this function is creating symmetry. Each square is paired with an element value. In the image below you can see the element values increment as they reach the center and then decrement. Squares with matching element values are drawn with the same color.

As create_square receives its parameters from create_invader, it uses a queue and the element values from before to ensure symmetry. The first occurrence of the values have their colors pushed onto the queue and the mirrored squares pop the colors off.

I realize how difficult it is to read through and understand someone else’s solution for a problem, and the roughness of the code certainly does not help with its complexity, but hopefully you’ve got a pretty good idea for how it works. Ultimately it would be incredible if you are able to scrap my code altogether and figure out an entirely different solution.

Conclusion

Generative art takes time to fully appreciate, but it’s worth it. I love being able to combine programming with a more traditional visual, and I have definitely learned a lot in every one of my projects.

Overall there may be more useful projects to pursue and generative art may not be something you need experience with, but it’s a ton of fun and you never know how it might separate you from the crowd.

Thank you for reading!