├── .gitignore ├── LICENSE ├── README.md ├── nb_output ├── generated_bw_3.png ├── generated_bw_6.png ├── generated_bw_7.png ├── generated_bw_9.png ├── generated_cmyk_10.png ├── generated_hsl_5.png ├── generated_hsl_8.png ├── generated_hsv_1.png ├── generated_rgb_2.png └── generated_rgb_4.png ├── nb_random_art.ipynb ├── nn.png ├── nnetart ├── __init__.py ├── artgen.py └── network.py ├── random_art.py ├── results ├── BW_generated1.png ├── BW_generated2.png ├── BW_generated3.png ├── BW_generated4.png ├── BW_generated5.png ├── CMYK_generated1.png ├── CMYK_generated2.png ├── CMYK_generated3.png ├── CMYK_generated4.png ├── CMYK_generated5.png ├── HSL_generated1.png ├── HSL_generated2.png ├── HSL_generated3.png ├── HSL_generated4.png ├── HSL_generated5.png ├── HSV_generated1.png ├── HSV_generated2.png ├── HSV_generated3.png ├── HSV_generated4.png ├── HSV_generated5.png ├── RGB_generated1.png ├── RGB_generated10.png ├── RGB_generated2.png ├── RGB_generated3.png ├── RGB_generated4.png ├── RGB_generated5.png ├── RGB_generated6.png ├── RGB_generated7.png ├── RGB_generated8.png ├── RGB_generated9.png ├── deepRGB.png └── verydeepRGB.png └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | nnetart.egg-info/* 2 | 3 | results/ 4 | 5 | .DS_Store 6 | .idea/* 7 | 8 | nnetart/__pycache__/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tuan Le 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 | # neural-net-random-art 2 | Create a grayscale or colour image with predefined size `img_height` and `img_width` using fully connected neural networks. 3 | The generation of images only requires python `numpy`, `torch` and `matplotlib`. 4 | Medium article can be found [here](https://medium.com/@tuanle618/generate-abstract-random-art-with-a-neural-network-ecef26f3dd5f). 5 | # Usage 6 | You can either have a look at the jupyter notebook [nb_random_art.ipynb](https://github.com/tuanle618/neural-net-random-art/blob/master/nb_random_art.ipynb) if you want to understand the algorithm and check out several settings of the method. 7 | For fast image generation is is recommended to use the python main programm file [random_art.py](https://github.com/tuanle618/neural-net-random-art/blob/master/random_art.py) 8 | 9 | # Using Google Colab 10 | I've created a minimal working example on Google Colab here [here](https://colab.research.google.com/drive/1TFmQQOUHOPjSrB0dVeoYiD7d7FPidilW?usp=sharing) in case you want to experiment with this library a little bit, prior to installing it on your local machine. 11 | To generate an animation by applying small gradual changes in the neural net when generating consecutive new images, check out this [notebook](https://colab.research.google.com/drive/1Jdmvh72NsQlb0GdXdhgBDMfiNm5Q8WUe?usp=sharing). 12 | 13 | # Dependencies: Python 3 14 | ``` 15 | numpy 16 | matplotlib 17 | seaborn 18 | torch 19 | ``` 20 | 21 | # Installation 22 | ``` 23 | git clone https://github.com/tuanle618/neural-net-random-art.git 24 | cd neural-net-random-art 25 | pip install -e . 26 | ``` 27 | 28 | # Execution 29 | 30 | For the `random_art.py` program `argparse` is used to define several input parameters: 31 | ```python 3 32 | parser = argparse.ArgumentParser(description="Generate random art with a deep neural network") 33 | 34 | parser.add_argument("-img_height", metavar="", type=int, default=512, 35 | help="Image height of created random art. Default is 512") 36 | 37 | parser.add_argument("-img_width", metavar="", type=int, default=512, 38 | help="Image width of created random art. Default is 512") 39 | 40 | parser.add_argument("-colormode", metavar="", type=str, default="rgb", 41 | help="How image color should be generated. Options are ['bw', 'rgb', 'cmyk', 'hsv', 'hsl']." 42 | " By default this value is 'rgb'") 43 | 44 | parser.add_argument("-alpha", metavar="", type=str, default="False", 45 | help="Whether or not to add a alpha channel for the image. Default is False") 46 | 47 | parser.add_argument("-n_images", metavar="", type=int, default=1, 48 | help="Number of images to generate. Default is 1") 49 | 50 | parser.add_argument("-n_depth", metavar="", type=int, default=5, 51 | help="Number of layers for the neural network. Default is 5") 52 | 53 | parser.add_argument("-n_size", metavar="", type=int, default=10, 54 | help="Number of neurons in each hidden layer. Default is 10") 55 | 56 | parser.add_argument("-activation", metavar="", type=str, default="tanh", 57 | help="Activation function to apply on the hidden layers. Default is 'tanh'") 58 | 59 | parser.add_argument("-z1", metavar="", type=float, default=-0.618, 60 | help="Input variable 1 to insert determinism into the random art." 61 | " The value should be between -1 and 1. Default is -0.618") 62 | 63 | parser.add_argument("-z2", metavar="", type=float, default=+0.618, 64 | help="Input variable 2 to insert determinism into the random art." 65 | " The value should be between -1 and 1. Default is +0.618") 66 | 67 | parser.add_argument("-trig", metavar="", type=str, default="True", 68 | help="If the z1 and z2 values should be transformed with cosine and sine respectively. " 69 | "Defaults to True.") 70 | 71 | parser.add_argument("-noise", metavar="", type=str, default="False", 72 | help="If gaussian noise should be added for the generated image. Defaults to False") 73 | 74 | parser.add_argument("-noise_std", metavar="", type=float, default=0.01, 75 | help="Gaussian noise standard deviation if it should be added to the generated image. " 76 | " Defaults to 0.01.") 77 | 78 | parser.add_argument("-sym", metavar="", type=str, default="False", 79 | help="Use symmetry network. Default is False") 80 | 81 | parser.add_argument("-gpu", metavar="", type=str, default="False", 82 | help="Use GPU to generate (vectorized) image. Defaults to False") 83 | 84 | parser.add_argument("-format", metavar="", type=str, default="png", 85 | help="File format to save the images. Defaults to 'png'." 86 | "Choices are 'pnd', 'jpg', 'pdf' and 'svg'.", 87 | choices=['png', 'jpg', 'svg', 'pdf'] 88 | ) 89 | ``` 90 | 91 | So in order to create 1 RGB image of size 400x500, no alpha channel, a dense net with 15 layers, each laying having 15 neurons, type in following command in the shell: 92 | 93 | ` 94 | python random_art.py -img_height 400 -img_width 500 -colormode rgb -alpha False -n_images 1 -n_depth 15 -n_size 15 95 | ` 96 | 97 | # Examples 98 | Following commands were used [default params were used] to get the images stored in the [result subdirectory](https://github.com/tuanle618/neural-net-random-art/tree/master/results): 99 | ``` 100 | python random_art.py -img_height 512 -image_width 512 -colormode bw -alpha False -n_images 5 101 | ``` 102 | E.g, leading to following 2 random images (resized in order to have next to each other): 103 |

104 | 105 | 106 |

107 | 108 | ``` 109 | python random_art.py -img_height 512 -img_width 512 -colormode rgb -alpha False -n_images 10 110 | ``` 111 | 112 | E.g, leading to following 2 random images (resized in order to have next to each other): 113 |

114 | 115 | 116 |

117 | 118 | ``` 119 | python random_art.py -img_height 512 -img_width 512 -colormode cmyk -alpha False -n_images 5 120 | ``` 121 | 122 | E.g, leading to following 2 random images (resized in order to have next to each other): 123 |

124 | 125 | 126 |

127 | 128 | ``` 129 | python random_art.py -img_height 512 -img_width 512 -colormode hsv -alpha False -n_images 5 130 | ``` 131 | 132 | E.g, leading to following 2 random images (resized in order to have next to each other): 133 |

134 | 135 | 136 |

137 | 138 | ``` 139 | python random_art.py -img_height 512 -img_width 512 -colormode hsl -alpha False -n_images 5 140 | ``` 141 | 142 | E.g, leading to following 2 random images (resized in order to have next to each other): 143 |

144 | 145 | 146 |

147 | 148 | You can try out different input arguments (larger networks and neurons, different actiation functions etc..) as suggested in the Jupyter Notebook, to see what images will be created. 149 | For example the following images are created by deeper neural nets. 150 | Image 1: `n_depth=15` and `n_size=25`, Image 2: `n_depth=25` and `n_size=45`: 151 |

152 | 153 | 154 |

155 | 156 | ------ 157 | 158 | ### If you like this repo, feel free to ⭐ and share it! 159 | 160 | 161 | # License 162 | Code under MIT License. 163 | -------------------------------------------------------------------------------- /nb_output/generated_bw_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_bw_3.png -------------------------------------------------------------------------------- /nb_output/generated_bw_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_bw_6.png -------------------------------------------------------------------------------- /nb_output/generated_bw_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_bw_7.png -------------------------------------------------------------------------------- /nb_output/generated_bw_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_bw_9.png -------------------------------------------------------------------------------- /nb_output/generated_cmyk_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_cmyk_10.png -------------------------------------------------------------------------------- /nb_output/generated_hsl_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_hsl_5.png -------------------------------------------------------------------------------- /nb_output/generated_hsl_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_hsl_8.png -------------------------------------------------------------------------------- /nb_output/generated_hsv_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_hsv_1.png -------------------------------------------------------------------------------- /nb_output/generated_rgb_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_rgb_2.png -------------------------------------------------------------------------------- /nb_output/generated_rgb_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nb_output/generated_rgb_4.png -------------------------------------------------------------------------------- /nn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nn.png -------------------------------------------------------------------------------- /nnetart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/nnetart/__init__.py -------------------------------------------------------------------------------- /nnetart/artgen.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @title: random_art.py 4 | @author: Tuan Le 5 | @email: tuanle@hotmail.de 6 | 7 | Minimal image pre-processing script 8 | """ 9 | 10 | import numpy as np 11 | import torch 12 | import multiprocessing as mp 13 | from multiprocessing import Pool 14 | 15 | 16 | def process_xy_el_meshgrid(xv: np.ndarray, 17 | yv: np.ndarray, 18 | symmetry: bool, 19 | trig: bool, 20 | z1: float, 21 | z2: float) -> torch.Tensor: 22 | """ 23 | Transforms the input tuple (x,y) 24 | Args: 25 | xv: numpy array (width, height) 26 | yv: (width, height) 27 | symmetry: boolean, if x and y should be symmetric, i.e. squaring the tuple elements 28 | trig: if the input tuples should be transformed using the cosine and sine function. Note this returns more 29 | variability, for the z1 and z2 values, respectively. 30 | z1: deterministic factor z1, if trig is set to False. 31 | z2: deterministic factor z2, if trig is set to False. 32 | 33 | Returns: np.array with 5 columns, for the values x,y,r,z1,z2 34 | """ 35 | # radius part r 36 | if symmetry: 37 | xv = xv**2 38 | yv = yv**2 39 | r_ = np.sqrt(xv**2 + yv**2) 40 | # z1, z2 part 41 | if trig: 42 | z1_ = np.cos(z1*xv) 43 | z2_ = np.sin(z2*yv) 44 | else: 45 | z1_ = np.empty_like(xv, dtype=np.float32) 46 | z1_.fill(z1) 47 | z2_ = np.empty_like(yv, dtype=np.float32) 48 | z2_.fill(z2) 49 | 50 | x_ = np.expand_dims(xv.T.flatten(), axis=1) 51 | y_ = np.expand_dims(yv.T.flatten(), axis=1) 52 | r_ = np.expand_dims(r_.T.flatten(), axis=1) 53 | z1_ = np.expand_dims(z1_.T.flatten(), axis=1) 54 | z2_ = np.expand_dims(z2_.T.flatten(), axis=1) 55 | 56 | # create flattened image 57 | res = np.concatenate([x_, y_, r_, z1_, z2_], axis=1) 58 | return torch.from_numpy(res).float() 59 | 60 | 61 | def init_data(img_height: int = 500, 62 | img_width: int = 700, 63 | symmetry: bool = False, 64 | trig: bool = True, 65 | z1: float = -0.618, z2: float = 0.618, 66 | noise: float = False, 67 | noise_std: float = 0.01): 68 | factor = min(img_height, img_width) 69 | # get input: x,y,r,z_1,z_2 70 | x = [(i/factor-0.5)*2 for i in range(img_height)] 71 | y = [(j/factor-0.5)*2 for j in range(img_width)] 72 | 73 | xv, yv = np.meshgrid(x, y) 74 | in_data = process_xy_el_meshgrid(xv, yv, symmetry, trig, z1, z2) 75 | if noise: 76 | in_data += torch.zeros_like(in_data).normal_(mean=0, std=noise_std) 77 | return in_data 78 | 79 | 80 | def hsv_to_rgb(h, s, v): 81 | # hsw are between 0 and 1 82 | # returns rgb between 0 and 1 83 | # from: https://bgrins.github.io/TinyColor/docs/tinycolor.html 84 | h *= 6 85 | i = np.floor(h) 86 | f = h - i 87 | p = v * (1 - s) 88 | q = v * (1 - f * s) 89 | t = v * (1 - (1 - f) * s) 90 | mod = int(i % 6) 91 | r = [v, q, p, p, t, v][mod] 92 | g = [t, v, v, q, p, p][mod] 93 | b = [p, p, t, v, v, q][mod] 94 | return r, g, b 95 | 96 | 97 | def hsv_to_rgb_torch(img: torch.Tensor) -> torch.Tensor: 98 | # hsw are between 0 and 1 99 | # returns rgb between 0 and 1 100 | # from: https://bgrins.github.io/TinyColor/docs/tinycolor.html 101 | h_ = img[:, :, 0].view(img.size(0) * img.size(1)).detach().data.numpy() 102 | s_ = img[:, :, 1].view(img.size(0) * img.size(1)).detach().data.numpy() 103 | v_ = img[:, :, 2].view(img.size(0) * img.size(1)).detach().data.numpy() 104 | h = 6 * h_ 105 | i = np.floor(h) 106 | f = h - i 107 | p = (v_ * (1 - s_)) 108 | q = (v_ * (1 - f * s_)) 109 | t = (v_ * (1 - (1 - f) * s_)) 110 | mod = [int(a % 6) for a in i] 111 | r_select = torch.Tensor([[v_[i], q[i], p[i], p[i], t[i], v_[i]][m] for i, m in enumerate(mod)]).view(img.size(0), 112 | img.size( 113 | 1)).unsqueeze( 114 | -1) 115 | g_select = torch.Tensor([[t[i], v_[i], v_[i], q[i], p[i], p[i]][m] for i, m in enumerate(mod)]).view(img.size(0), 116 | img.size( 117 | 1)).unsqueeze( 118 | -1) 119 | b_select = torch.Tensor([[p[i], p[i], t[i], v_[i], v_[i], q[i]][m] for i, m in enumerate(mod)]).view(img.size(0), 120 | img.size( 121 | 1)).unsqueeze( 122 | -1) 123 | img = torch.cat([r_select, g_select, b_select], dim=-1) 124 | 125 | return img 126 | 127 | 128 | def hue_to_rgb(p, q, t): 129 | if t < 0 or t > 1: 130 | return p 131 | if t < 1 / 6: 132 | return p + (q - p) * 6 * t 133 | if t < 1 / 2: 134 | return q 135 | if t < 2 / 3: 136 | return p + (q - p) * (2 / 3 - t) * 6 137 | else: 138 | return p 139 | 140 | 141 | def hsl_to_rgb(h, s, l): 142 | # hsl are between 0 and 1 143 | # returns rgb between 0 and 1 144 | if s == 0: 145 | r = g = b = l # achromatic 146 | else: 147 | if l < 0.5: 148 | q = l * (1 + s) 149 | else: 150 | q = l + s - l * s 151 | 152 | p = 2 * l - q 153 | r = hue_to_rgb(p, q, h + 1 / 3) 154 | g = hue_to_rgb(p, q, h) 155 | b = hue_to_rgb(p, q, h - 1 / 3) 156 | 157 | return r, g, b 158 | 159 | 160 | def hsl_to_rgb_torch(h: torch.Tensor, s: torch.Tensor, l: torch.Tensor): 161 | h = h.cpu().data.numpy() 162 | s = s.cpu().data.numpy() 163 | l = l.cpu().data.numpy() 164 | with Pool(processes=2) as pool: 165 | proc_img = pool.starmap(hsl_to_rgb, zip(h, s, l)) 166 | proc_img = torch.Tensor(proc_img) 167 | return proc_img 168 | 169 | 170 | def transform_colors(img: torch.Tensor, colormode: str, alpha: bool): 171 | if alpha: 172 | alpha_tensor = img[:, :, -1] 173 | # Since non blackmode [rgb, cmyk, hsv, hsl] values are mapped onto [0,1] the alpha channel is also between [0,1]. 174 | # 0=transparency, 1=opaque wrt. to overlaying 175 | a = 1 - torch.abs(2 * alpha_tensor - 1) 176 | a = (0.25 + 0.75 * a).unsqueeze(-1) 177 | else: 178 | a = torch.ones(size=(img.size(0), img.size(1))).unsqueeze(-1) 179 | 180 | if colormode == "rgb": # Output via sigmoid activation mapped into range [0,1] 181 | proc_img = img[:, :, 0:2] 182 | elif colormode == "bw": 183 | proc_img = torch.cat([img[:, :, 0].unsqueeze(-1)] * 3, dim=-1) 184 | elif colormode == "cmyk": 185 | r = ((1 - img[:, :, 0]) * img[:, :, 3]).unsqueeze(-1) 186 | g = ((1 - img[:, :, 1]) * img[:, :, 3]).unsqueeze(-1) 187 | b = ((1 - img[:, :, 2]) * img[:, :, 3]).unsqueeze(-1) 188 | proc_img = torch.cat([r, g, b], dim=-1).to(img.device) 189 | elif colormode == "hsv": 190 | proc_img = hsv_to_rgb_torch(img) 191 | elif colormode == "hsl": 192 | h = img[:, :, 0].view(img.size(0) * img.size(1)) 193 | s = img[:, :, 1].view(img.size(0) * img.size(1)) 194 | l = img[:, :, 2].view(img.size(0) * img.size(1)) 195 | proc_img = hsl_to_rgb_torch(h, s, l).to(img.device) 196 | proc_img = proc_img.view(img.size(0), img.size(1), 3) 197 | else: 198 | print("Inserted colormode '{}' is not part ob supported ones: [rgb, bw, cmyk, hsv, hsl]".format(colormode)) 199 | raise Exception("Non-supported colormode {}".format(colormode)) 200 | 201 | res = torch.cat([proc_img, a], dim=-1) 202 | 203 | return res -------------------------------------------------------------------------------- /nnetart/network.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @title: random_art.py 4 | @author: Tuan Le 5 | @email: tuanle@hotmail.de 6 | 7 | Implements a simple feed-forward neural network 8 | """ 9 | 10 | import torch 11 | import torch.nn as nn 12 | 13 | from typing import List 14 | 15 | 16 | def weights_init_normal(m: nn.Module): 17 | """ 18 | Change the standard deviations for the weight-attribute if you wanna experiment further. 19 | """ 20 | classname = m.__class__.__name__ 21 | if classname.find('Linear') != -1: 22 | m.weight.data.normal_(0.0, 1.0) 23 | m.bias.data.normal_(0.0, 0.1) 24 | return None 25 | 26 | 27 | def init_activation_fnc(a): 28 | if a == "tanh": 29 | return nn.Tanh() 30 | elif a == "sigmoid": 31 | return nn.Sigmoid() 32 | elif a == "relu": 33 | return nn.ReLU() 34 | elif a == "softsign": 35 | return nn.Softsign() 36 | elif a == "sin": 37 | return torch.sin 38 | elif a == "cos": 39 | return torch.cos 40 | else: 41 | print(f"Inserted activation function {a} not compatible. Using tanh.") 42 | return nn.Tanh() 43 | 44 | 45 | class FeedForwardNetwork(nn.Module): 46 | def __init__(self, 47 | layers_dims: List = [10, 10, 10, 10, 10], 48 | activation_fnc: str = "tanh", 49 | colormode: str = "rgb", 50 | alpha: bool = True): 51 | super(FeedForwardNetwork, self).__init__() 52 | colormode = colormode.lower() 53 | if colormode in ["rgb", "hsv", "hsl"]: 54 | if not alpha: 55 | out_nodes = 3 56 | else: 57 | out_nodes = 4 58 | elif colormode == "cmyk": 59 | if not alpha: 60 | out_nodes = 4 61 | else: 62 | out_nodes = 5 63 | elif colormode == "bw": 64 | if not alpha: 65 | out_nodes = 1 66 | else: 67 | out_nodes = 2 68 | else: 69 | print(f"wrong colormode {colormode} inserted in Neural Net. initialization.") 70 | raise ValueError 71 | input_layer = nn.Linear(in_features=5, out_features=layers_dims[0], bias=True) 72 | output_layer = nn.Linear(in_features=layers_dims[-1], out_features=out_nodes, bias=True) 73 | self.layers = nn.ModuleList([input_layer] + 74 | [nn.Linear(in_features=layers_dims[i], 75 | out_features=layers_dims[i+1], bias=True) 76 | for i in range(len(layers_dims)-1)] + 77 | [output_layer] 78 | ) 79 | self.activation = init_activation_fnc(activation_fnc.lower()) 80 | self.apply(weights_init_normal) 81 | 82 | def forward(self, x: torch.Tensor) -> torch.Tensor: 83 | out = x 84 | for i, m in enumerate(self.layers): 85 | out = m(out) 86 | if i < len(self.layers)-1: 87 | out = self.activation(out) 88 | else: 89 | out = torch.sigmoid(out) 90 | return out -------------------------------------------------------------------------------- /random_art.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @title: random_art.py 4 | @author: Tuan Le 5 | @email: tuanle@hotmail.de 6 | 7 | Main python programme to generate random images using feedforward nets 8 | """ 9 | 10 | import os 11 | import sys 12 | import time 13 | import json 14 | import argparse 15 | import matplotlib 16 | import matplotlib.pyplot as plt 17 | 18 | import torch 19 | from nnetart.network import FeedForwardNetwork 20 | from nnetart.artgen import init_data, transform_colors 21 | 22 | 23 | def generate_image_torch(my_net: FeedForwardNetwork = FeedForwardNetwork(), 24 | img_height=512, 25 | img_width=512, 26 | symmetry=False, 27 | trig=True, 28 | colormode="rgb", 29 | alpha=True, 30 | z1=-0.618, z2=0.618, 31 | show=True, 32 | fname="netart", 33 | format="png", 34 | save=True, 35 | gpu=False, 36 | with_noise=False, 37 | noise_std=0.01): 38 | input_data = init_data(img_height, img_width, symmetry, trig, z1, z2, noise=with_noise, noise_std=noise_std) 39 | if gpu: 40 | input_data = input_data.cuda() 41 | my_net = my_net.cuda() 42 | 43 | with torch.no_grad(): 44 | img = my_net(input_data) 45 | 46 | if gpu: 47 | img = img.cpu() 48 | 49 | img = img.view(img_height, img_width, img.size(-1)) 50 | img = transform_colors(img, colormode, alpha) 51 | img = img.numpy() 52 | if not show: 53 | matplotlib.use("Agg") 54 | plt.figure() 55 | fig = plt.imshow(img, interpolation="bilinear", aspect="auto") 56 | plt.axis("off") 57 | fig.axes.get_xaxis().set_visible(False) 58 | fig.axes.get_yaxis().set_visible(False) 59 | if show: 60 | plt.show() 61 | if save: 62 | plt.imsave(f"{fname}", img, format=format) 63 | return img 64 | 65 | 66 | def args_parser(): 67 | parser = argparse.ArgumentParser(description="Generate random art with a deep neural network") 68 | 69 | parser.add_argument("-img_height", metavar="", type=int, default=512, 70 | help="Image height of created random art. Default is 512") 71 | 72 | parser.add_argument("-img_width", metavar="", type=int, default=512, 73 | help="Image width of created random art. Default is 512") 74 | 75 | parser.add_argument("-colormode", metavar="", type=str, default="rgb", 76 | help="How image color should be generated. Options are ['bw', 'rgb', 'cmyk', 'hsv', 'hsl']." 77 | " By default this value is 'rgb'") 78 | 79 | parser.add_argument("-alpha", metavar="", type=str, default="True", 80 | help="Whether or not to add a alpha channel for the image. Default is True") 81 | 82 | parser.add_argument("-n_images", metavar="", type=int, default=1, 83 | help="Number of images to generate. Default is 1") 84 | 85 | parser.add_argument("-n_depth", metavar="", type=int, default=5, 86 | help="Number of layers for the neural network. Default is 5") 87 | 88 | parser.add_argument("-n_size", metavar="", type=int, default=10, 89 | help="Number of neurons in each hidden layer. Default is 10") 90 | 91 | parser.add_argument("-activation", metavar="", type=str, default="tanh", 92 | help="Activation function to apply on the hidden layers. Default is 'tanh'") 93 | 94 | parser.add_argument("-z1", metavar="", type=float, default=-0.618, 95 | help="Input variable 1 to insert determinism into the random art." 96 | " The value should be between -1 and 1. Default is -0.618") 97 | 98 | parser.add_argument("-z2", metavar="", type=float, default=+0.618, 99 | help="Input variable 2 to insert determinism into the random art." 100 | " The value should be between -1 and 1. Default is +0.618") 101 | 102 | parser.add_argument("-trig", metavar="", type=str, default="True", 103 | help="If the z1 and z2 values should be transformed with cosine and sine respectively. " 104 | "Defaults to True.") 105 | 106 | parser.add_argument("-noise", metavar="", type=str, default="False", 107 | help="If gaussian noise should be added for the generated image. Defaults to False") 108 | 109 | parser.add_argument("-noise_std", metavar="", type=float, default=0.01, 110 | help="Gaussian noise standard deviation if it should be added to the generated image. " 111 | " Defaults to 0.01.") 112 | 113 | parser.add_argument("-sym", metavar="", type=str, default="False", 114 | help="Use symmetry network. Default is False") 115 | 116 | parser.add_argument("-gpu", metavar="", type=str, default="False", 117 | help="Use GPU to generate (vectorized) image. Defaults to False") 118 | 119 | parser.add_argument("-format", metavar="", type=str, default="png", 120 | help="File format to save the images. Defaults to 'png'." 121 | "Choices are 'pnd', 'jpg', 'pdf' and 'svg'.", 122 | choices=['png', 'jpg', 'svg', 'pdf'] 123 | ) 124 | 125 | args = parser.parse_args() 126 | 127 | return args 128 | 129 | 130 | def info_print(args): 131 | """ 132 | This function prints the input arguments from argparse when calling this script via python shell. 133 | Args: 134 | args [argparse.Namespace]: argument namespace from main.py 135 | Returns: 136 | None 137 | """ 138 | print(50*"-") 139 | print("Random Art with Deep Neural Networks:") 140 | print(50*"-") 141 | print("Script Arguments:") 142 | print(25*"-") 143 | for arg in vars(args): 144 | print(arg, ":", getattr(args, arg)) 145 | print(25*"-") 146 | return None 147 | 148 | 149 | def str_to_bool(s): 150 | """ 151 | This function converts a string into a boolean value 152 | Args: 153 | s [str]: string representing tre value 154 | Returns: 155 | b [bool]: boolean value of string representation 156 | 157 | """ 158 | if s.lower() in ["true", "yes", "y", "t", "1"]: 159 | b = True 160 | elif s.lower() in ["false", "no", "f", "n", "0"]: 161 | b = False 162 | else: 163 | print("boolean string not correctly specified") 164 | sys.exit(1) 165 | return b 166 | 167 | 168 | def main(): 169 | # retrieve arguments and print out in shell 170 | args = args_parser() 171 | # print out information on shell 172 | info_print(args) 173 | 174 | # Params # 175 | img_height = args.img_height 176 | img_width = args.img_width 177 | colormode = args.colormode.lower() 178 | alpha = str_to_bool(args.alpha) 179 | n_images = args.n_images 180 | n_depth = args.n_depth 181 | n_size = args.n_size 182 | activation = args.activation 183 | z1 = args.z1 184 | z2 = args.z2 185 | symmetry = str_to_bool(args.sym) 186 | trig = str_to_bool(args.trig) 187 | gpu = str_to_bool(args.gpu) 188 | with_noise = str_to_bool(args.noise) 189 | noise_std = args.noise_std 190 | 191 | if not os.path.exists("results"): 192 | print("Creating subdirectory 'results'.") 193 | os.makedirs("results") 194 | 195 | save_dir = "results/" + time.strftime('%Y%m%d%H%M%S') 196 | 197 | os.makedirs(save_dir) 198 | with open(os.path.join(save_dir, 'commandline_args.txt'), 'w') as f: 199 | json.dump(args.__dict__, f, indent=2) 200 | 201 | for i in range(n_images): 202 | start_time = time.time() 203 | print("Generating image number {}...".format(i+1)) 204 | save_path = "{}/{}_generated{}.{}".format(save_dir, colormode, i+1, args.format) 205 | my_net = FeedForwardNetwork(layers_dims=[n_size]*n_depth, activation_fnc=activation, 206 | colormode=colormode.lower(), alpha=alpha) 207 | 208 | _ = generate_image_torch(my_net, img_height, img_width, symmetry=symmetry, 209 | trig=trig, colormode=colormode, 210 | alpha=alpha, z1=z1, z2=z2, with_noise=with_noise, noise_std=noise_std, 211 | show=False, fname=save_path, save=True, gpu=gpu) 212 | 213 | delta = time.time()-start_time 214 | print("Generating image took {} seconds".format(delta)) 215 | print("Image number {} saved at {}".format(i+1, save_path)) 216 | 217 | 218 | if __name__ == "__main__": 219 | main() -------------------------------------------------------------------------------- /results/BW_generated1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/BW_generated1.png -------------------------------------------------------------------------------- /results/BW_generated2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/BW_generated2.png -------------------------------------------------------------------------------- /results/BW_generated3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/BW_generated3.png -------------------------------------------------------------------------------- /results/BW_generated4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/BW_generated4.png -------------------------------------------------------------------------------- /results/BW_generated5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/BW_generated5.png -------------------------------------------------------------------------------- /results/CMYK_generated1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/CMYK_generated1.png -------------------------------------------------------------------------------- /results/CMYK_generated2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/CMYK_generated2.png -------------------------------------------------------------------------------- /results/CMYK_generated3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/CMYK_generated3.png -------------------------------------------------------------------------------- /results/CMYK_generated4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/CMYK_generated4.png -------------------------------------------------------------------------------- /results/CMYK_generated5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/CMYK_generated5.png -------------------------------------------------------------------------------- /results/HSL_generated1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSL_generated1.png -------------------------------------------------------------------------------- /results/HSL_generated2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSL_generated2.png -------------------------------------------------------------------------------- /results/HSL_generated3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSL_generated3.png -------------------------------------------------------------------------------- /results/HSL_generated4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSL_generated4.png -------------------------------------------------------------------------------- /results/HSL_generated5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSL_generated5.png -------------------------------------------------------------------------------- /results/HSV_generated1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSV_generated1.png -------------------------------------------------------------------------------- /results/HSV_generated2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSV_generated2.png -------------------------------------------------------------------------------- /results/HSV_generated3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSV_generated3.png -------------------------------------------------------------------------------- /results/HSV_generated4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSV_generated4.png -------------------------------------------------------------------------------- /results/HSV_generated5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/HSV_generated5.png -------------------------------------------------------------------------------- /results/RGB_generated1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated1.png -------------------------------------------------------------------------------- /results/RGB_generated10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated10.png -------------------------------------------------------------------------------- /results/RGB_generated2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated2.png -------------------------------------------------------------------------------- /results/RGB_generated3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated3.png -------------------------------------------------------------------------------- /results/RGB_generated4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated4.png -------------------------------------------------------------------------------- /results/RGB_generated5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated5.png -------------------------------------------------------------------------------- /results/RGB_generated6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated6.png -------------------------------------------------------------------------------- /results/RGB_generated7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated7.png -------------------------------------------------------------------------------- /results/RGB_generated8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated8.png -------------------------------------------------------------------------------- /results/RGB_generated9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/RGB_generated9.png -------------------------------------------------------------------------------- /results/deepRGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/deepRGB.png -------------------------------------------------------------------------------- /results/verydeepRGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuanle618/neural-net-random-art/e617d075104ee6bf28d5362b933c34bc1be019dd/results/verydeepRGB.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='nnetart', 5 | version='0.1', 6 | description='Generate random art with neural networks', 7 | author='Tuan Le', 8 | author_email='tuanle@hotmail.de', 9 | packages=find_packages(), install_requires=['torch', 'numpy', 'matplotlib', 'typing', 'seaborn'] 10 | ) 11 | --------------------------------------------------------------------------------