├── .gitignore ├── LICENSE ├── README.md ├── cppn.py └── examples └── image.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Thomas Hoch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cppn-keras 2 | Implementation of a [Compositional Pattern Producing Network](https://en.wikipedia.org/wiki/Compositional_pattern-producing_network) in Keras inspired by [cppn-tensorflow](https://github.com/hardmaru/cppn-tensorflow). 3 | 4 | 5 | Example: 6 | -------- 7 | 8 | ![Generated Image](https://raw.githubusercontent.com/hochthom/cppn-keras/master/examples/image.png) 9 | 10 | Have fun! 11 | -------------------------------------------------------------------------------- /cppn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 24 19:36:30 2016 4 | 5 | @author: hochthom 6 | """ 7 | 8 | import time 9 | import numpy as np 10 | from scipy.interpolate import interp1d 11 | from keras.models import Sequential 12 | from keras.layers import Dense, Activation 13 | from keras import initializations 14 | from PIL import Image 15 | 16 | 17 | 18 | def my_init(shape, name=None): 19 | return initializations.normal(shape, scale=1.2, name=name) 20 | 21 | def create_grid(x_dim=32, y_dim=32, scale=1.0): 22 | ''' 23 | returns a vector of x and y coordinates and th ecorresponding radius from 24 | the centre of the image. 25 | ''' 26 | N = 0.5*(x_dim + y_dim) 27 | x1 = np.linspace(-1.0*x_dim/N*scale, 1.0*x_dim/N*scale, x_dim) 28 | y1 = np.linspace(-1.0*y_dim/N*scale, 1.0*y_dim/N*scale, y_dim) 29 | X, Y = np.meshgrid(x1, y1) 30 | x1 = np.ravel(X).reshape(-1, 1) 31 | y1 = np.ravel(Y).reshape(-1, 1) 32 | r1 = np.sqrt(x1**2 + y1**2) 33 | return x1, y1, r1 34 | 35 | def interpolate_z(z, n_frames=25, mode=None): 36 | ''' 37 | Interpolate movement through latent space with spline approximation. 38 | ''' 39 | x_max = float(z.shape[0]) 40 | if mode is not None: 41 | x_max += 1 42 | if 'smooth' in mode: 43 | x_max += 2 44 | 45 | xx = np.arange(0, x_max) 46 | zt = [] 47 | for k in range(z.shape[1]): 48 | yy = list(z[:,k]) 49 | if mode is not None: 50 | yy.append(z[0,k]) 51 | if 'smooth' in mode: 52 | yy = [z[-1,k]] + yy + [z[1,k]] 53 | fz = interp1d(xx, yy, kind='cubic') 54 | if 'smooth' in mode: 55 | x_new = np.linspace(1, x_max-2, num=n_frames, endpoint=False) 56 | else: 57 | x_new = np.linspace(0, x_max-1, num=n_frames, endpoint=False) 58 | zt.append(fz(x_new)) 59 | 60 | return np.column_stack(zt) 61 | 62 | def create_image(model, x, y, r, z): 63 | ''' 64 | create an image for the given latent vector z 65 | ''' 66 | # create input vector 67 | Z = np.repeat(z, x.shape[0]).reshape((-1,x.shape[0])) 68 | X = np.concatenate([x, y, r, Z.T], axis=1) 69 | 70 | pred = model.predict(X) 71 | 72 | img = [] 73 | for k in range(pred.shape[1]): 74 | yp = pred[:, k] 75 | # if k == pred.shape[1]-1: 76 | # yp = np.sin(yp) 77 | yp = (yp - yp.min()) / (yp.max()-yp.min()) 78 | img.append(yp.reshape(y_dim, x_dim)) 79 | 80 | img = np.dstack(img) 81 | if img.shape[-1] == 3: 82 | from skimage.color import hsv2rgb 83 | img = hsv2rgb(img) 84 | 85 | return (img*255).astype(np.uint8) 86 | 87 | def create_image_seq(model, x, y, r, z, n_frames=25, mode=None): 88 | ''' 89 | create a list of images with n_frames between the given latent vectors in z 90 | ''' 91 | # create all z values 92 | zt = interpolate_z(z, n_frames, mode) 93 | 94 | images = [] 95 | for k in range(zt.shape[0]): 96 | print 'Creating image %3i/%i ...' % (k+1, zt.shape[0]) 97 | images.append(create_image(model,x,y,r,zt[k,:])) 98 | 99 | return images 100 | 101 | 102 | 103 | SINGLE_IMAGE = False 104 | COLORING = True 105 | 106 | n_z = 16 107 | x_dim = 720 108 | y_dim = 720 109 | x, y, r = create_grid(x_dim=x_dim, y_dim=y_dim, scale=5.0) 110 | # create latent space 111 | z = np.random.normal(0, 1, (3,n_z)) 112 | 113 | # create neural network 114 | n_l = 32 115 | model = Sequential([ 116 | Dense(n_l, init=my_init, input_dim=n_z+3), 117 | Activation('tanh'), 118 | Dense(n_l, init=my_init), 119 | Activation('tanh'), 120 | Dense(n_l, init=my_init), 121 | Activation('tanh'), 122 | Dense(3 if COLORING else 1), 123 | #Activation('sigmoid'), 124 | Activation('linear'), 125 | ]) 126 | 127 | model.compile(optimizer='rmsprop', loss='mse') 128 | 129 | 130 | # create images 131 | t_start = time.time() 132 | 133 | if SINGLE_IMAGE: 134 | img = create_image(model, x, y, r, z[0,:]) 135 | im = Image.fromarray(img) 136 | im.save('test.png') 137 | else: 138 | img_seq = create_image_seq(model, x, y, r, z, n_frames=100, mode='smooth') 139 | for k, img in enumerate(img_seq): 140 | im = Image.fromarray(img) 141 | im.save('seq/img_%03i.png' % k) 142 | 143 | if True: 144 | from moviepy.editor import * 145 | if COLORING: 146 | tmp = img_seq 147 | else: 148 | tmp = [np.dstack(3*[img]).astype("uint8") for img in img_seq] 149 | clip = ImageSequenceClip(tmp, fps=25) 150 | clip.write_videofile("video.avi", fps=25, codec='libx264') 151 | 152 | t_stop = time.time() 153 | print 'done in %.1f seconds.' % (t_stop-t_start) 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /examples/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hochthom/cppn-keras/b41f0f065d536cee1422ff000fe12892e43db3ab/examples/image.png --------------------------------------------------------------------------------