Як створити генеративне мистецтво менш ніж у 100 рядках коду

Генеративне мистецтво, як і будь-яка тема програмування, може лякати, якщо ви ніколи раніше не пробували його. Мене це завжди цікавило, бо я люблю знаходити нові способи творчого використання програмування. Крім того, я думаю, що кожен може оцінити концепцію твору мистецтва, який буквально створює себе.

Що таке генеративне мистецтво?

Генеративне мистецтво є результатом системи, яка сама приймає рішення щодо твору, а не людини. Система може бути такою самою простою, як одна програма Python, якщо вона має правила та деякі аспекти випадковості.

З програмуванням досить просто придумати правила та обмеження. Ось і всі умовні твердження. Сказавши це, знайти шляхи, щоб зробити ці правила щось цікаве, може бути складно.

«Гра життя» - це відомий набір з чотирьох простих правил, які визначають «народження» та «смерть» кожної клітини системи. Кожне з правил відіграє свою роль у просуванні системи через кожне покоління. Незважаючи на те, що правила прості і легкі для розуміння, складні моделі швидко починають з’являтися і, зрештою, дають захоплюючі результати.

Правила можуть нести відповідальність за створення фундаменту чогось цікавого, але навіть щось таке захоплююче, як Гра життя Конвея, передбачуване. Оскільки чотири правила є визначальними факторами для кожного покоління, спосіб отримання непередбачуваних результатів полягає у введенні рандомізації у вихідному стані клітин. Починаючи з випадкової матриці, кожне виконання стане унікальним, не потребуючи зміни правил.

Найкращими прикладами генеративного мистецтва є ті, що знаходять поєднання передбачуваності та випадковості, щоб створити щось цікаве, що також є статистично невідтворюваним .

Чому ви повинні спробувати?

Не всі побічні проекти створені рівними, і генеративне мистецтво може бути не тим, на що ви схильні витрачати час. Однак, якщо ви вирішите працювати над проектом, ви можете очікувати таких переваг:

  • Досвід - генеративне мистецтво - це лише чергова можливість вдосконалити деякі нові та старі навички. Він може служити шлюзом для практикування таких понять, як алгоритми, структури даних і навіть нові мови.
  • Відчутні результати - у світі програмування ми рідко бачимо, що щось фізичне виходить із наших зусиль, або принаймні я цього не роблю. Зараз у мене у вітальні є кілька плакатів із зображеннями мого генеративного мистецтва, і я люблю, що за це відповідає програмування.
  • Привабливі проекти - Ми всі мали досвід пояснення особистого проекту комусь, можливо навіть під час співбесіди, без простого способу передати зусилля та результати проекту. Генеративне мистецтво говорить саме за себе, і більшість людей будуть вражені вашими творіннями, навіть якщо вони не можуть повністю зрозуміти методи.

З чого почати?

Початок роботи з генеративним мистецтвом - такий самий процес, як і будь-який проект, найважливіший крок - це придумати ідею або знайти, на якій можна будувати. Після того, як ви поставили перед собою мету, ви можете почати працювати над технологією, необхідною для її досягнення.

Більшість моїх генеративних мистецьких проектів було виконано в Python. Це досить проста мова, до якої можна звикнути, і в ньому є кілька неймовірних пакетів, які допоможуть у маніпуляціях із зображеннями, наприклад, Подушка.

На щастя для вас, немає необхідності шукати відправну точку дуже далеко, тому що я надав трохи коду нижче, з яким ви можете грати.

Спрайт-генератор

Цей проект розпочався, коли я побачив пост, в якому демонструється генератор спрайтів, написаний на Javascript. Програма створила 5 -5-піксельні мистецькі спрайти з деякими випадковими варіантами кольорів, і її результати нагадували різнокольорові космічні загарбники.

Я знав, що хочу практикувати маніпуляції із зображеннями в Python, тому вирішив, що можу просто спробувати відтворити цю концепцію самостійно. Крім того, я думав, що можу продовжити його, оскільки початковий проект був настільки обмежений у розмірі спрайтів. Я хотів мати можливість вказати не тільки розмір, а й кількість їх і навіть розмір зображення.

Ось огляд двох різних результатів рішення, з яким я опинився:

Ці два зображення взагалі не нагадують одне одного, але обидва вони є результатами однієї системи. Не кажучи вже про те, що через складність зображення та випадковість генерації спрайтів існує надзвичайно висока ймовірність того, що навіть за однакових аргументів ці зображення назавжди стануть єдиними у своєму роді. Я це люблю.

Навколишнє середовище

Якщо ви хочете почати бавитися з генератором спрайтів, спочатку потрібно виконати невелику фундаментальну роботу.

Налаштування належного середовища за допомогою Python може бути складним. Якщо ви раніше не працювали з Python, можливо, вам доведеться завантажити Python 2.7.10. Спочатку у мене були проблеми з налаштуванням середовища, тому якщо у вас виникнуть проблеми, ви можете зробити те, що я зробив, і заглянути у віртуальне середовище. І останнє, але не менш важливе: переконайтеся, що у вас також встановлена ​​Подушка.

Після налаштування середовища ви можете скопіювати мій код у файл із розширенням .py та виконати за допомогою наступної команди:

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

Наприклад, команда для створення першої матриці спрайтів зверху буде такою:

python spritething.py 7 30 1900

Кодекс

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

Це рішення є далеким від ідеального, але воно показує, що для створення генеративного мистецтва не потрібно багато тонн коду. Я спробую з усіх сил пояснити основні моменти.

Основна функція починається зі створення вихідного зображення і визначення розміру спрайтів. Два цикли for відповідають за визначення кордону для кожного спрайту, в основному поділяючи розміри зображення на кількість спрайтів. Ці значення використовуються для визначення координат для кожного з них.

Давайте проігноруємо заповнення та подивимось на зображення нижче. Уявіть, що кожен з чотирьох квадратів представляє спрайт розміром 1. Кордон, який передається наступній функції, відноситься до верхньої лівої та нижньої правої координат. Тож кортеж для верхнього лівого спрайту буде (0,0,1,1), тоді як кортеж для верхнього правого буде (1,0,2,1). Вони будуть використовуватися як розміри та базові координати для квадратів кожного спрайта.

Функція create_invader визначає межу для кожного квадрата в спрайті. Той самий процес визначення межі застосовується тут і представлений нижче, лише замість повного зображення ми використовуємо заздалегідь визначену межу для роботи всередині. Ці остаточні координати для кожного квадрата будуть використані в наступній функції для фактичного малювання спрайта.

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!