#!/usr/bin/env python3
from numpy import *
from PIL import Image, ImageDraw, ImageFont
import random
random.seed("Perlin")
fnt = ImageFont.truetype('Courier Prime.ttf', 100)
XDIM, YDIM = 100, 100 # gradient mesh size (must be big enough for highest octave)
XDP, YDP = 1024, 1024 # Perlin noise array size
phi = zeros((YDIM, XDIM)) # gradient phase angles
perlin = zeros((YDP, XDP)) # Perlin noise array
# choose random phase angles for gradients
for y in range(YDIM):
for x in range(XDIM):
phi[y,x] = 2 * pi * random.random()
def grad(x0, y0, dx, dy):
"Compute the dot product between the gradient and position vector"
gx, gy = cos(phi[y0,x0]), sin(phi[y0,x0])
return gx * dx + gy * dy
def inter(a, b, w):
"Interpolate between a and b using weight w"
return (b - a) * ((w * (w * 6 - 15) + 10) * w * w * w) + a # smootherstep
# compute Perlin noise in several octaves and add it together
for nscale, SCALE in enumerate((512, 256, 128, 64, 32, 16)):
for yi in range(YDP):
for xi in range(XDP):
x, y = xi / SCALE, yi / SCALE
x0, y0 = int(x), int(y)
dx, dy = x - x0, y - y0
g1 = grad(x0, y0, dx, dy)
g2 = grad(x0 + 1, y0, -1 + dx, dy)
g3 = grad(x0, y0 + 1, dx, -1 + dy)
g4 = grad(x0 + 1, y0 + 1, -1 + dx, -1 + dy)
perlin[yi,xi] += (.5**nscale) * inter(inter(g1, g2, dx), inter(g3, g4, dx), dy)
p2 = (perlin * 128 + 128).astype(uint8)
im = Image.fromarray(p2, mode = "L")
d = ImageDraw.Draw(im)
d.text((0, 0), "%u" % (nscale + 1), font = fnt, fill = 255, align = "left")
im.save("out%u.png" % nscale)
print("saved out%u.png" % nscale)
# Create GIF animation with ImageMagick:
# convert -delay 150 out*.png -loop 0 perlin_animation_6_octaves.gif