├── .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 |
--------------------------------------------------------------------------------