วิธีสร้าง Generative Art ในโค้ดน้อยกว่า 100 บรรทัด

ศิลปะแบบกำเนิดเช่นหัวข้อการเขียนโปรแกรมอาจเป็นเรื่องที่น่าวิตกหากคุณไม่เคยลองมาก่อน ฉันสนใจเรื่องนี้มาโดยตลอดเพราะฉันชอบค้นหาวิธีการใหม่ ๆ ที่สามารถใช้ประโยชน์จากการเขียนโปรแกรมได้อย่างสร้างสรรค์ นอกจากนี้ฉันคิดว่าทุกคนสามารถชื่นชมแนวคิดของงานศิลปะที่สร้างขึ้นเองอย่างแท้จริง

Generative Art คืออะไร?

Generative Art คือผลลัพธ์ของระบบที่ตัดสินใจเกี่ยวกับชิ้นส่วนนั้น ๆ แทนที่จะเป็นมนุษย์ ระบบอาจเรียบง่ายเหมือนโปรแกรม Python ตัวเดียวตราบใดที่มีกฎและลักษณะบางอย่างของการสุ่ม

ด้วยการเขียนโปรแกรมมันค่อนข้างตรงไปตรงมาที่จะสร้างกฎและข้อ จำกัด นั่นคือคำแถลงเงื่อนไขทั้งหมด ต้องบอกว่าการหาวิธีทำให้กฎเหล่านี้สร้างสิ่งที่น่าสนใจอาจเป็นเรื่องยุ่งยาก

เกมแห่งชีวิตเป็นกฎง่ายๆสี่ข้อที่มีชื่อเสียงซึ่งกำหนด "การเกิด" และ "การตาย" ของแต่ละเซลล์ในระบบ กฎแต่ละข้อมีส่วนในการพัฒนาระบบในแต่ละรุ่น แม้ว่ากฎจะเรียบง่ายและเข้าใจง่าย แต่รูปแบบที่ซับซ้อนก็เริ่มปรากฏขึ้นอย่างรวดเร็วและก่อให้เกิดผลลัพธ์ที่น่าสนใจในที่สุด

กฎอาจมีส่วนรับผิดชอบในการสร้างรากฐานของสิ่งที่น่าสนใจ แต่สิ่งที่น่าตื่นเต้นอย่าง Game of Life ของ Conway ก็สามารถคาดเดาได้ เนื่องจากกฎทั้งสี่เป็นปัจจัยกำหนดสำหรับแต่ละรุ่นวิธีที่จะให้ผลลัพธ์ที่ไม่อาจคาดเดาได้คือการนำการสุ่มที่สถานะเริ่มต้นของเซลล์ การเริ่มต้นด้วยเมทริกซ์แบบสุ่มจะทำให้การดำเนินการแต่ละครั้งไม่ซ้ำกันโดยไม่จำเป็นต้องเปลี่ยนกฎ

ตัวอย่างที่ดีที่สุดของศิลปะการกำเนิดเป็นคนที่พบว่าการรวมกันของการคาดการณ์และการสุ่มเพื่อสร้างสิ่งที่น่าสนใจที่ยังเป็นสถิติระหว่างทำการ

ทำไมคุณควรลอง?

โครงการด้านข้างทั้งหมดไม่ได้สร้างขึ้นเท่ากันและงานศิลปะที่สร้างขึ้นอาจไม่ใช่สิ่งที่คุณต้องการใช้เวลา อย่างไรก็ตามหากคุณตัดสินใจที่จะทำงานในโครงการคุณจะได้รับประโยชน์เหล่านี้:

  • ประสบการณ์ - ศิลปะทั่วไปเป็นเพียงโอกาสในการฝึกฝนทักษะใหม่และเก่า สามารถใช้เป็นประตูสู่การฝึกฝนแนวคิดเช่นอัลกอริทึมโครงสร้างข้อมูลและแม้แต่ภาษาใหม่ ๆ
  • ผลลัพธ์ที่เป็นรูปธรรม - ในโลกของการเขียนโปรแกรมเราแทบจะไม่ได้เห็นสิ่งใดที่เกิดขึ้นจริงจากความพยายามของเราหรืออย่างน้อยฉันก็ไม่เห็น ตอนนี้ฉันมีโปสเตอร์สองสามชิ้นในห้องนั่งเล่นของฉันที่แสดงภาพพิมพ์ของศิลปะกำเนิดของฉันและฉันชอบที่รายการมีส่วนรับผิดชอบ
  • โครงการที่น่าดึงดูด - เราทุกคนเคยมีประสบการณ์ในการอธิบายโครงการส่วนตัวให้ใครบางคนฟังแม้กระทั่งในระหว่างการสัมภาษณ์โดยไม่มีวิธีง่ายๆในการถ่ายทอดความพยายามและผลลัพธ์ของโครงการ ศิลปะแบบกำเนิดพูดเพื่อตัวมันเองและทุกคนจะประทับใจกับการสร้างสรรค์ของคุณแม้ว่าพวกเขาจะไม่สามารถเข้าใจวิธีการได้ทั้งหมดก็ตาม

คุณควรเริ่มจากตรงไหน?

การเริ่มต้นสร้างงานศิลปะเป็นกระบวนการเดียวกับโครงการใด ๆ ขั้นตอนที่สำคัญที่สุดคือการคิดหรือหาสิ่งที่จะสร้างขึ้น เมื่อคุณมีเป้าหมายในใจแล้วคุณสามารถเริ่มทำงานกับเทคโนโลยีที่จำเป็นในการบรรลุเป้าหมายได้

โครงการศิลปะกำเนิดส่วนใหญ่ของฉันประสบความสำเร็จใน Python เป็นภาษาที่ค่อนข้างง่ายในการทำความคุ้นเคยและมีแพ็คเกจที่น่าทึ่งเพื่อช่วยในการจัดการภาพเช่น Pillow

โชคดีสำหรับคุณไม่จำเป็นต้องค้นหาจุดเริ่มต้นให้ไกลนักเพราะเราได้ให้โค้ดไว้ด้านล่างเพื่อให้คุณเล่นด้วย

เครื่องกำเนิดสไปรท์

โปรเจ็กต์นี้เริ่มต้นเมื่อฉันเห็นโพสต์ที่อวดตัวสร้างสไปรต์ที่เขียนด้วย Javascript โปรแกรมสร้างสไปรต์อาร์ต 5x5 พิกเซลพร้อมตัวเลือกสีแบบสุ่มและผลลัพธ์ของมันคล้ายกับผู้บุกรุกพื้นที่หลายสี

ฉันรู้ว่าฉันต้องการฝึกการปรับแต่งภาพใน 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]))

โซลูชันนี้เป็นวิธีที่ยาวนานจากความสมบูรณ์แบบ แต่ก็แสดงให้เห็นว่าการสร้างงานศิลปะแบบกำเนิดนั้นไม่ได้ใช้รหัสมากมาย ฉันจะพยายามอย่างดีที่สุดเพื่ออธิบายประเด็นสำคัญ

หลักฟังก์ชั่นเริ่มต้นด้วยการสร้างภาพเริ่มต้นและการกำหนดขนาดของสไปรท์ที่ ทั้งสองสำหรับลูปมีหน้าที่กำหนดเส้นขอบสำหรับสไปรต์แต่ละตัวโดยทั่วไปจะแบ่งขนาดของภาพตามจำนวนสไปรต์ที่ร้องขอ ค่าเหล่านี้ใช้เพื่อกำหนดพิกัดสำหรับแต่ละค่า

Let’s ignore padding and take a look at the image below. Imagine that each of the four squares represents a sprite with a size of 1. The border that is being passed to the next function refers to the top left and bottom right coordinates. So the tuple for the top left sprite would be (0,0,1,1) whereas the tuple for the top right would be (1,0,2,1). These will be used as the dimensions and base coordinates for the squares of each sprite.

The function create_invader determines the border for each square within the sprite. The same process for determining the border is applied here and represented below, only instead of the full image we’re using a pre-determined border to work inside. These final coordinates for each square will be used in the next function to actually draw the 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!