Getting to grips with vectorization in py5

@solub, @tabreturn @villares thanks for some kind help from @hx2A I’m making progress with numpy and image processing in py5. Recently there has been some discussion about the quality of different noise implementations, this a sketch after an example by @hx2A that explores UniformNoise (java), vnoise (python) and OpenSimplex2 (java).

import py5
import numpy as np
from PIL import Image
import noise
import vnoise

OpenSimplex2S = py5.JClass('monkstone.noise.OpenSimplex2S')
UniformNoise = py5.JClass('micycle.uniformnoise.UniformNoise')

w, h = 1200, 800

vector_noise = vnoise.Noise()
xgrid, ygrid = np.meshgrid(np.linspace(0, 12 // 3, num=w // 3, dtype=np.float32), np.linspace(0, 12, num=h, dtype=np.float32))
noise_array = np.full((h, w // 3, 3), 255, dtype=np.uint8)
noise_array2 = noise_array.copy()
noise_array3 = noise_array.copy()

def setup():
    py5.size(w, h)
    global open_simplex, uniform_noise
    open_simplex = OpenSimplex2S(py5.millis())
    uniform_noise = UniformNoise()

def draw():
    # UniformNoise by micycle
    noise_array[:, :, 0] = 255 * (np.vectorize(uniform_noise.uniformNoise)(xgrid, ygrid, py5.frame_count * 0.01, 4, 0.5))
    py5.image(Image.fromarray(noise_array, mode='HSV').convert('RGB'), 0, 0)
    # vnoise library
    noise_array2[:, :, 0] = 255 * (vector_noise.noise3(ygrid, xgrid, py5.frame_count * 0.1, octaves=1, grid_mode=False) + 1) / 2
    py5.image(Image.fromarray(noise_array2, mode='HSV').convert('RGB'), 400, 0)
    # open simplex
    noise_array3[:, :, 0] = 255 * (np.vectorize(open_simplex.noise3_Classic)(ygrid, xgrid, py5.frame_count * 0.1) + 1) / 2
    py5.image(Image.fromarray(noise_array3, mode='HSV').convert('RGB'), 800, 0)

py5.run_sketch()

Output:-

4 Likes

Excellent work! It is interesting to see the improvements in noise quality provided by algorithms like uniform noise.

1 Like

Note that your comparing 4-octave noise from UniformNoise vs 1 octave in the other two.

How do you specify the number of octaves using UniformNoise?

I knew that, I thought it was sort of optimum. It’s probably a bit confusing to have all the parameters in the noise function, given the possibility of 1d, 2d, 3d, 4d and possibly higher dimensions. As a rubyist anything more than say 4 parameters is frowned on. In this case you could have default values for octave, and persistence?