clumpy icon indicating copy to clipboard operation
clumpy copied to clipboard

create or transform numpy images from the command line

Build Status GitHub license

This tool can manipulate or generate large swaths of image data stored in numpy files. It's a sandbox for implementing operations in C++ that are either slow or non-existent in pillow, scikit-image, or the SciPy ecosystem.

Since it's just a command line tool, it doesn't contain any FFI messiness. Feel free to contribute by adding your own command, but keep it simple! Add a cc file to the commands folder and make a pull request.

This is just a toy library. For serious C++ applications you might want to look at xtensor (which can read / write npy files) and xtensor-io. To achieve huge speed-ups with numpy, see numba.


Build and run clumpy.

cmake -H. -B.release -GNinja && cmake --build .release
alias clumpy=$PWD/.release/clumpy
clumpy help

Generate two octaves of simplex noise and combine them.

clumpy generate_simplex 500x250 0.5 16.0 0 noise1.npy
clumpy generate_simplex 500x250 1.0 8.0  0 noise2.npy

python <<EOL
import numpy as np; from PIL import Image
noise1, noise2 = np.load("noise1.npy"), np.load("noise2.npy")
result = np.clip(np.abs(noise1 + noise2), 0, 1)
Image.fromarray(np.uint8(result * 255), "L").show()
EOL

Create a distance field with a random shape.

clumpy generate_dshapes 500x250 1 0 shapes.npy
clumpy visualize_sdf shapes.npy rgb shapeviz.npy

python <<EOL
import numpy as np; from PIL import Image
Image.fromarray(np.load('shapeviz.npy'), 'RGB').show()
EOL

Create a 2x2 atlas of distance fields, each with 5 random shapes.

for i in {1..4}; do clumpy generate_dshapes 250x125 5 $i shapes$i.npy; done
for i in {1..4}; do clumpy visualize_sdf shapes$i.npy shapes$i.npy; done

python <<EOL
import numpy as np; from PIL import Image
a, b, c, d = (np.load('shapes{}.npy'.format(i)) for i in [1,2,3,4])
img = np.vstack(((np.hstack((a,b)), np.hstack((c,d)))))
Image.fromarray(img, 'RGB').show()
EOL

Create a nice distribution of ~20k points, cull points that overlap certain areas, and plot them. Do all this in less than a second and use only one thread.

clumpy bridson_points 500x250 2 0 coords.npy
clumpy generate_dshapes 500x250 1 0 shapes.npy
clumpy cull_points coords.npy shapes.npy culled.npy
clumpy splat_points culled.npy 500x250 u8disk 1 1.0 splats.npy

python <<EOL
import numpy as np; from PIL import Image
Image.fromarray(np.load("splats.npy"), "L").show()
EOL

You may wish to invoke clumpy from within Python using os.system or subprocess.Popen .

Here's an example that generates 240 frames of an advection animation with ~12k points, then brightens up the last frame and displays it. This entire script takes about 1 second to execute and uses only one core (3.1 GHz Intel Core i7).

from numpy import load
from PIL import Image
from os import system

def clumpy(cmd):
    result = system('./clumpy ' + cmd)
    if result: raise Exception("clumpy failed with: " + cmd)

clumpy('generate_simplex 1000x500 1.0 8.0 0 potential.npy')
clumpy('curl_2d potential.npy velocity.npy')
clumpy('bridson_points 1000x500 5 0 pts.npy')
clumpy('advect_points pts.npy velocity.npy 30 1 0.95 240 anim.npy')
Image.fromarray(load("000anim.npy"), "L").point(lambda p: p * 2).show()

Create a visualization of pendulum's phase space.

clumpy pendulum_phase 4000x2000 0.9 2 5 field.npy
clumpy bridson_points 4000x2000 20 0 pts.npy
clumpy advect_points pts.npy field.npy 2.5 5 0.99 400 phase.npy