In [1]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
We can easily plot images by using the imshow
function. Conveniently, an image can just be a 2-dimensional numpy array of floats in the range of 0 to 1. We can easily create such an array with the ranf
function. Below we create a 128x128 image of random values and display that using the ocean
colormap from the pyplot.cm
module.
In [2]:
img = np.random.ranf((128,128))
plt.imshow(img, cmap=plt.cm.ocean)
Out[2]:
The image above is an example of random noise. Although useful we need something a bit more controllable. For this we need a function that produces value noise instead of just random noise. Essentially this is just a function that given a vector of inputs, produces an float in the range of 0 to 1. For now we'll focus on one-dimensional noise but we'll deal with noise in more dimensions later.
There's various ways to produce value noise but we're gonna use a method that utilizes a seeded vector of random values. For no particular reason we're going to call this vector r
and we'll use a numpy array to hold it. For convenience we will also write a function seed
that we can use to initialize the table.
In [3]:
def seed(n, shape=(8,)):
global r
np.random.seed(n)
r = np.random.ranf(shape)
We can visualize r
by plotting it.
In [4]:
seed(0)
x = np.arange(0, len(r), 1)
plt.plot(x, r[x], 'bo')
plt.axis('tight')
Out[4]:
We can use this table to define a function noise
that given an integer value x
will return a value in the range of 0 to 1. The r
vector only has a limited amount of values. In order to make it work for any value of x
we can perform a modulo operation with the length of the r
vector.
In [5]:
def noise(x):
x = int(x % len(r))
return r[x]
By default we only have 8 values in the r
vector so if we plot the noise function over a range 0 to 16 we can see that it repeats itself halfway.
In [6]:
x = np.arange(0, 16, 1)
y = [noise(x) for x in x]
plt.plot(x, y, 'bo')
Out[6]:
Our noise
function works for integer values of x
ideally it works for real values of x
as well. We can do this by interpolation.
In [7]:
def noise(x):
xi = int(x)
x0 = xi % len(r)
x1 = 0 if x0 == (len(r) - 1) else (x0 + 1)
v0, v1 = r[x0], r[x1]
t = x - xi
return np.interp(t, [0, 1], [v0, v1])
In [8]:
x = np.arange(0, 16, 1/10)
y = [noise(x) for x in x]
plt.plot(x, y)
Out[8]:
Now let's create some noise in two-dimensions. We'll implement a naive solution at first and revisit it later. Let's start by re-seeding the r
table in two dimensions.
In [9]:
seed(0, (4,4))
And now we redefine our noise
function so it will interpolate between the x
and y
value as well.
In [10]:
def noise(x, y):
xi, yi = int(x), int(y)
tx, ty = x - xi, y - yi
x0 = xi % len(r)
x1 = 0 if x0 == (len(r) - 1) else x0 + 1
y0 = yi % len(r[0])
y1 = 0 if y0 == (len(r) - 1) else y0 + 1
c00 = r[x0][y0]
c01 = r[x1][y0]
c10 = r[x0][y1]
c11 = r[x1][y1]
n0 = np.interp(tx, [0, 1], [c00, c01])
n1 = np.interp(tx, [0, 1], [c10, c11])
return np.interp(ty, [0, 1], [n0, n1])
In [11]:
img = np.array([[noise(x, y) for x in range(16)] for y in range(16)])
plt.imshow(img, plt.cm.ocean)
Out[11]:
We can see clearly see a pattern here. That's because the dimensions of our r
table are very small, only 4 in each axis. We'll get much better noise if we make it bigger.
In [12]:
seed(0, (16,16))
img = np.array([[noise(x, y) for x in range(32)] for y in range(32)])
plt.imshow(img, cmap=plt.cm.ocean)
Out[12]:
There's still a pattern but that is to be expected with our still reasonably small r
vector. It does like a lot better already but another thing that is noticeable is that it appears to be a bit blocky. This is because of the linear interpolation we're using to calculate the final noise values. We can fix this by introducing the smoothstep
function.
In [13]:
def smoothstep(t):
return 3 * t**2 - 2 * t**3
In [14]:
x = np.linspace(0, 1, 1000)
y = smoothstep(x)
plt.plot(x, y)
Out[14]:
And now we have to adjust our noise function to incorporate the smoothstep
function.
In [15]:
def noise(x, y, c = lambda t: t):
xi, yi = int(x), int(y)
tx, ty = x - xi, y - yi
x0 = xi % len(r)
x1 = 0 if x0 == (len(r) - 1) else x0 + 1
y0 = yi % len(r[0])
y1 = 0 if y0 == (len(r) - 1) else y0 + 1
c00 = r[x0][y0]
c01 = r[x1][y0]
c10 = r[x0][y1]
c11 = r[x1][y1]
sx = c(tx)
sy = c(ty)
n0 = np.interp(sx, [0, 1], [c00, c01])
n1 = np.interp(sx, [0, 1], [c10, c11])
return np.interp(sy, [0, 1], [n0, n1])
Note that instead of interpolating on tx
and ty
directly we are using the smoothstep
-ed values of sx
and sy
instead.
In [32]:
def gen_im(size=(64,64), c = lambda x: x):
w, h = size
pixels = [[noise(x, y, c) for x in range(w)] for y in range(h)]
return np.array(pixels)
In [35]:
seed(1, (32, 32))
img = gen_im(c = smoothstep)
plt.imshow(img, cmap=plt.cm.ocean)
Out[35]:
In [42]:
MAX_VERTICES = 32
MAX_VERTICES_MASK = MAX_VERTICES - 1
In [ ]: