Sådan oprettes generativ kunst i mindre end 100 linjer med kode

Generativ kunst, som ethvert programmeringsemne, kan være skræmmende, hvis du aldrig har prøvet det før. Jeg har altid været interesseret i det, fordi jeg elsker at finde nye måder, hvorpå programmering kan bruges kreativt. Desuden tror jeg, at alle kan sætte pris på begrebet kunst, der bogstaveligt talt skaber sig selv.

Hvad er generativ kunst?

Generativ kunst er output fra et system, der træffer sine egne beslutninger om stykket snarere end et menneske. Systemet kan være så simpelt som et enkelt Python-program, så længe det har regler og et eller andet aspekt af tilfældighed.

Med programmering er det ret ligetil at komme med regler og begrænsninger. Det er alt betinget udsagn. Når det er sagt, kan det være vanskeligt at finde måder at få disse regler til at skabe noget interessant.

Livets spil er et berømt sæt med fire enkle regler, der bestemmer "fødsel" og "død" for hver celle i systemet. Hver af reglerne spiller en rolle i at fremme systemet gennem hver generation. Selvom reglerne er enkle og lette at forstå, begynder komplekse mønstre hurtigt at dukke op og i sidste ende danne fascinerende resultater.

Regler kan være ansvarlige for at skabe fundamentet for noget interessant, men endda noget så spændende som Conways Game of Life er forudsigeligt. Da de fire regler er de afgørende faktorer for hver generation, er måden at producere uforudsigelige resultater på at indføre randomisering i cellernes starttilstand. Begyndende med en tilfældig matrix gør hver udførelse unik uden at skulle ændre reglerne.

De bedste eksempler på generativ kunst er dem, der finder en kombination af forudsigelighed og tilfældighed for at skabe noget interessant, der også er statistisk irreproducerbart .

Hvorfor skulle du prøve det?

Ikke alle sideprojekter er skabt ens, og generativ kunst er muligvis ikke noget, du er tilbøjelig til at bruge tid på. Hvis du dog beslutter at arbejde på et projekt, kan du forvente disse fordele:

  • Erfaring - Generativ kunst er bare endnu en mulighed for at finpudse nogle nye og gamle færdigheder. Det kan tjene som en gateway til at øve koncepter som algoritmer, datastrukturer og endda nye sprog.
  • Håndgribelige resultater - I programmeringsverdenen ser vi sjældent noget fysisk ud af vores indsats, eller i det mindste gør jeg det ikke. Lige nu har jeg et par plakater i min stue, der viser udskrifter af min generative kunst, og jeg elsker, at programmering er ansvarlig for det.
  • Attraktive projekter - Vi har alle haft oplevelsen af ​​at forklare et personligt projekt til nogen, muligvis endda under et interview uden en nem måde at formidle projektets indsats og resultater på. Generativ kunst taler for sig selv, og de fleste vil blive imponeret over dine kreationer, selvom de ikke helt kan forstå metoderne.

Hvor skal du starte?

At komme i gang med generativ kunst er den samme proces som ethvert projekt, det mest afgørende skridt er at komme med en idé eller finde en at bygge videre på. Når du først har et mål i tankerne, kan du begynde at arbejde på den teknologi, der kræves for at nå det.

De fleste af mine generative kunstprojekter er gennemført i Python. Det er et ret nemt sprog at vænne sig til, og det har nogle utrolige pakker til rådighed til at hjælpe med billedmanipulation, såsom Pillow.

Heldigvis for dig er der ingen grund til at søge meget langt efter et udgangspunkt, fordi jeg har angivet noget kode nedenfor, som du kan lege med.

Sprite Generator

Dette projekt startede, da jeg så et indlæg, der viste en sprite-generator skrevet i Javascript. Programmet skabte 5x5 pixel kunstsprites med nogle tilfældige farveindstillinger, og dets output lignede flerfarvede rumindtrængere.

Jeg vidste, at jeg ville øve billedmanipulation i Python, så jeg regnede med, at jeg bare kunne prøve at genskabe dette koncept alene. Derudover troede jeg, at jeg kunne udvide det, da det oprindelige projekt var så begrænset i størrelsen af ​​sprites. Jeg ville være i stand til at specificere ikke kun størrelsen, men også antallet af dem og endda størrelsen på billedet.

Her er et kig på to forskellige output fra den løsning, jeg endte med:

Disse to billeder ligner slet ikke hinanden, men de er begge resultaterne af det samme system. For ikke at nævne på grund af billedets kompleksitet og sprite-generationens tilfældighed er der en ekstrem høj sandsynlighed for, at selv med de samme argumenter vil disse billeder for altid være enestående. Jeg elsker det.

Miljøet

Hvis du vil begynde at lege med sprite-generatoren, er der et lille fundamentarbejde, der skal udføres først.

Det kan være besværligt at oprette et ordentligt miljø med Python. Hvis du ikke har arbejdet med Python før, skal du sandsynligvis downloade Python 2.7.10. Jeg havde oprindeligt problemer med at oprette miljøet, så hvis du begynder at løbe ind i problemer, kan du gøre hvad jeg gjorde og se på virtuelle miljøer. Sidst men ikke mindst, sørg for at du også har Pillow installeret.

Når du har konfigureret miljøet, kan du kopiere min kode til en fil med filtypen .py og udføre med følgende kommando:

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

For eksempel vil kommandoen til at oprette den første matrix af sprites ovenfra være:

python spritething.py 7 30 1900

Koden

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

Denne løsning er langt fra perfekt, men den viser, at oprettelse af generativ kunst ikke tager et ton kode. Jeg vil gøre mit bedste for at forklare nøgleelementerne.

Den vigtigste funktion starter ved at skabe den indledende billede og bestemme størrelsen af sprites. De to for sløjfer er ansvarlige for at definere en ramme for hver sprite, grundlæggende dividere billedets dimensioner med antallet af anmodede sprites. Disse værdier bruges til at bestemme koordinaterne for hver enkelt.

Lad os ignorere polstring og se på billedet nedenfor. Forestil dig, at hver af de fire firkanter repræsenterer en sprite med en størrelse på 1. Grænsen, der sendes til den næste funktion refererer til øverste venstre og nederste højre koordinater. Så tuplen for den øverste venstre sprite ville være (0,0,1,1), mens den tuple for den øverste højre ville være (1,0,2,1). Disse vil blive brugt som dimensioner og basiskoordinater for firkanterne for hver sprite.

Funktionen create_invader bestemmer grænsen for hver firkant i sprite. Den samme proces til bestemmelse af grænsen anvendes her og er repræsenteret nedenfor, kun i stedet for det fulde billede bruger vi en forudbestemt kant til at arbejde indeni. Disse endelige koordinater for hvert kvadrat vil blive brugt i den næste funktion til faktisk at tegne 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.

Jeg er klar over, hvor svært det er at læse igennem og forstå en andens løsning på et problem, og grovheden af ​​koden hjælper bestemt ikke med dens kompleksitet, men forhåbentlig har du en ret god idé til, hvordan det fungerer. I sidste ende ville det være utroligt, hvis du er i stand til at skrotte min kode helt og finde ud af en helt anden løsning.

Konklusion

Generativ kunst tager tid at fuldt ud værdsætte, men det er det værd. Jeg elsker at kunne kombinere programmering med et mere traditionelt visuelt, og jeg har bestemt lært meget i hvert af mine projekter.

Samlet set kan der være mere nyttige projekter at forfølge, og generativ kunst er måske ikke noget, du har brug for erfaring med, men det er masser af sjov, og du ved aldrig, hvordan det kan adskille dig fra mængden.

Tak fordi du læste!